COM-порт и таймауты
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.142] |
|
|
Правила раздела Visual C++ / MFC / WTL (далее Раздела)
FAQ Раздела
Обновления для FAQ Раздела
Поиск по Разделу
MSDN Library Online
COM-порт и таймауты
|
Сообщ.
#1
,
|
|
|
|
Доброго дня всем!
Спешу к вам за помощью. Вопрос касается организации приема/передачи данных из COM-порта в использованием таймаутов. Перед выполнением нижеследующей функции я устанавливаю следующие параметры порта: ![]() ![]() // установка параметров порта GetCommState(hPort, &dcbPort); dcbPort.BaudRate = CBR_9600; dcbPort.fBinary = TRUE; dcbPort.fNull = FALSE; dcbPort.Parity = NOPARITY; dcbPort.ByteSize = 8; dcbPort.StopBits = ONESTOPBIT; SetCommState(hPort, &dcbPort); // таймауты не использую Вначале приведу работающий кусок кода из моей программы без использования таймаутов: ![]() ![]() void OnDOWNLOAD() { int count = 0; unsigned char dataInPort[6]; // буфер для записи unsigned char dataFromPort[256];// буфер для чтения for ( int i=0 ; i<6 ; i++ ) dataInPort[i] = 0; for ( int j=0 ; j<256 ; j++ ) dataFromPort[j] = 0; HANDLE hPort = {0}; // дескриптор порта DCB dcbPort = {0}; // свойства порта COMMTIMEOUTS tm = {0}; // таймауты OVERLAPPED over = {0}; // параметры асинхронной передачи COMSTAT state = {0}; // параметры приема/передачи CFile magEvents; // файл, куда записываются данные DWORD dwWrite = 0; DWORD dwRead = 0; DWORD readed = 0; DWORD dwWait = 0; DWORD errorMask = 0; DWORD eventMask = 0; for ( ; ; ) { dataInPort[0] = 0x01; dataInPort[1] = 0x18; dataInPort[2] = 0x00; dataInPort[3] = 0x00; dataInPort[4] = 0x81; dataInPort[5] = 0xDF; HANDLE hPort; // дескриптор порта DCB dcbPort; // дескриптор структуры свойств порта OVERLAPPED over = {0}; COMSTAT state = {0}; CFile magEvents; DWORD errorMask = 0; hPort = CreateFile("COM1", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if ( hPort==INVALID_HANDLE_VALUE ) { AfxMessageBox("Порт не настроен!\nДля настройки воспользуйтесь меню:\n\"Настройки\\COM-порт\"."); CloseHandle(hPort); break; } else { PurgeComm(hPort, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); GetCommState(hPort, &dcbPort); // запись в порт dwWrite = 0; WriteFile(hPort, &dataInPort, 6, &dwWrite, &over); // чтение из порта SetCommMask(hPort, EV_RXCHAR); over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); eventMask = 0; int wce = WaitCommEvent(hPort, &eventMask, &over); if ( (!wce) && (GetLastError()==ERROR_IO_PENDING) ) WaitForSingleObject(hPort, 500/*см. ниже "вопрос 1"*/); if ( eventMask & EV_RXCHAR ) { dwRead = 0; readed = 0; ClearCommError(hPort, &errorMask, &state); ReadFile(hPort, &dataFromPort, state.cbInQue, &dwRead, &over); GetOverlappedResult(hPort, &over, &readed, FALSE); ResetEvent(over.hEvent); unsigned short registerCount = 0; unsigned short byteCount = 0; registerCount = (dataFromPort[4] <<8 | dataFromPort[5]); byteCount = (dataFromPort[2] <<8 | dataFromPort[3]); if ( registerCount==0 && count==0 ) { AfxMessageBox("Данных в журнале нет"); CloseHandle(hPort); break; } if ( registerCount == 0 ) { AfxMessageBox("Журнал сохранен"); CloseHandle(hPort); break; } count += 1; ///////////////////////// сортировка данных и сохранение их в файл //не обращайте внимания на этот кусок unsigned char* data; data = new unsigned char[255]; for ( int count=0 ; count<=( (int)byteCount-2 ) ; count++ ) data[count] = dataFromPort[count+6]; magEvents.Open("events.mag", CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite, NULL); magEvents.Seek(0, CFile::end); magEvents.Write(data, ((int)byteCount-2) ); magEvents.Close(); delete data; ////////////////////////////////////////////////////////////////// } PurgeComm(hPort, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); CloseHandle(hPort); } } } Вопрос 1: У меня данных максимум может идти до 256 байт, так вот за эти 500 мс я успеваю (в случае прихода всех 256 байт) полностью их успеть прочитать при скорости 9600. Вопрос 2: (вытекает из вопроса 1) использование вышеприведенной реализации функции с использованием 500мс (см. вопрос 1) не совсем то, что мне надо (хотя все работает корректно!!). Появилась на необходимость использования таймаутов и с ними я никак ваще никак не могу раобраться. Помогите в етом плиз. Там де я заполняю структуру DCB там же я устанавливаю значения тайимаутов, например такие: ![]() ![]() // установка таймаутов (для скорости 9600) GetCommTimeouts(hPort, &tm); tm.ReadIntervalTimeout = 4; tm.ReadTotalTimeoutConstant = 100; tm.ReadTotalTimeoutMultiplier = 0; tm.WriteTotalTimeoutConstant = 0; tm.WriteTotalTimeoutMultiplier = 4; SetCommTimeouts(hPort, &tm); вот я установил....и что дальше я не могу понять, как мне переделать мою функцию чтобы при превышении вот этих таймаутов заканчивалась процедура записи/чтения. Нужно мне все это для того, чтобы: 1. если девайс не отвечает, программа не висла. Типа ждем заданный интервал времени и если байт не появляется, выдаем нечто типа: m_static.SetWindowText("Девайс не отвечает"); и повторять это сообщение надо до тех пор пока девайс не станет отвечать. В случае, если девайс отвечает и идет обмен данными, выводить типа: m_static.SetWindowText("Девайс в работе"); 2. ну и для того, чтобы удовлетворить моего руководителя. . Хотя пункт 1 - это для самоудовлетворения |
|
Сообщ.
#2
,
|
|
|
|
неужели никто ничего не подскажет ?
![]() понятно что лень даже до конца читать пост ![]() ps: не использовать таймауты нельзя надо один раз просто с этим разобраться ! |
|
Сообщ.
#3
,
|
|
|
|
Хм, в чем проблема-то? Если функция SetPortTimeouts() отработала успешно, а за положенное таймаутами время из порта не был получен байт/пакет (см.в MSDN), то функция ReadFile() вернет FALSE. Вот и проверяй результат - коли ЛОЖЬ, то девайс тебе не ответил, коли ПРАВДА, то обмен идет успешно.
Кстати, зачем ты в каждой итерации открываешь и закрываешь порт? Имхо достаточно сделать это один раз перед началом обмена и закрыть после окончания обмена. |
|
Сообщ.
#4
,
|
|
|
|
Вопрос 1)
Да уж...корректного я тут не увидел. Уж прости При вызове WaitCommEvent ты теряешь статус завершённости асинх. операции записи. А запись вообще завершилась? что-то я этого не вижу... А если она отвалила с ошибкой записи последнего байта, то чего-же ты будешь ждать на WaitCommEvent? На той стороне понимают незавершённые запросы? Да, в твоём случае ты легко можешь пролететь по всему твоему циклу, и повторить запись. Но это разве корректно??? Функция WaitCommEvent в оверлаппед-структуре требует наличия manual-reset эвента, а ты какой создал? При ожидании на WaitForSingleObject(hPort, 500) ты ждёшь завершения WaitCommEvent? Ты в этом точно уверен??? А может PFILE_OBJECT::hEvent всё ещё настроен после вызова WriteFile? Шансы зайти в обработку условия (eventMask & EV_RXCHAR) согласен огромные, но c учётом ранее написанного - они разве 100%? Такое смелое указывание state.cbInQue при возможном превышении sizeof( dataFromPort) c хоть и малой, но возможной долей вероятности схлопотать ошибку доступа на запись - это тож корректно? Указывание употреблённой, и не прочищенной оверлаппед-структуры (с непонятным статусом hEvent) в ReadFile - это корректно? GetOverlappedResult с указанным флагом FALSE (не дожидатся завершения асинх. операции) тут для чего? Ты разве определяешь, что операция ещё ERROR_IO_INCOMPLETE? А уж последующий вызов ResetEvent на возможно не установленный, и находящийся в прогрессе выполнения ReadFile - это вообще шедевр... Помойму ты достаточно плохо понимаешь асинхронные вызовы. Почиталб что-нить на эту тему... Вопрос 2) Насчет таймаутов - внимательно прочти следующее: Цитата If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one of the following occurs when the ReadFile function is called: If there are any characters in the input buffer, ReadFile returns immediately with the characters in the buffer. If there are no characters in the input buffer, ReadFile waits until a character arrives and then returns immediately. If no character arrives within the time specified by ReadTotalTimeoutConstant, ReadFile times out. |
|
Сообщ.
#5
,
|
|
|
|
я рад за тебя, AlexSm, что ты такой умный....только вот так вот разговаривать не надо ни с кем!
Чем вот так пальцы веером пускать, лучше бы помог разобраться как все это сделать действительно корректно и правильно!! Может у тебя опыта в этом вопросе и достаточно, то я за это берусь впервые... и я не видел ни одного человека который бы что нибудь с первого раза сделал бы правильно. |
|
Сообщ.
#6
,
|
|
|
|
AlexSm
Цитата AlexSm @ так он , вроде , такой и создал... Функция WaitCommEvent в оверлаппед-структуре требует наличия manual-reset эвента, а ты какой создал? ![]() emp по-моему, не стоит смешивать таймауты и асинхронные операции .... надо использовать либо то, либо другое. |
|
Сообщ.
#7
,
|
|
|
|
Цитата emp @ я рад за тебя, AlexSm Ты лучше сделай поиск с выражением: COM+GetCommState - и тебе всё станет ясно, я надеюсь; можно ещё в FAQ заглянуть! Также, >>>ещё<<< можно глянуть на сообщение под №8 ;D |
|
Сообщ.
#8
,
|
|
|
|
2AlexSm: В прошлом своем посте я погорячился раньше времени, сорри за это.
товарищи я проанализировал все ваши советы и вот к чему пришел (насколько это правильно с вашей точки зрения и чего не хватает мне?). Хто возьмется проверить код и подсказать исчо чего нить ? %) ![]() ![]() void OnDOWNLOAD() { int count = 0; unsigned char dataInPort[6]; // буфер для записи unsigned char dataFromPort[256];// буфер для чтения for ( int i=0 ; i<6 ; i++ ) dataInPort[i] = 0; for ( int j=0 ; j<6 ; j++ ) dataFromPort[j] = 0; HANDLE hPort = {0}; // дескриптор порта DCB dcbPort = {0}; // свойства порта COMMTIMEOUTS tm = {0}; // таймауты OVERLAPPED over = {0}; // параметры асинхронной передачи COMSTAT state = {0}; // параметры приема/передачи CFile magEvents; // файл, куда записываются данные DWORD dwWrite = 0; // фактическое число записанных байт DWORD dwRead = 0; // фактическое число прочитанных байт DWORD dwWait = 0; DWORD readed = 0; DWORD eventMask = 0; DWORD errorMask = 0; DWORD timeout = 500; // открытие порта hPort = CreateFile("COM1", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if ( hPort==INVALID_HANDLE_VALUE ) AfxMessageBox("Ошибка инициализации порта"); else { // очистка порта от старой информации int pc = PurgeComm(hPort, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); if ( pc==0 ) AfxMessageBox("Ошибка PurgeComm()"); else { int gcs = GetCommState(hPort, &dcbPort); if ( gcs==0 ) AfxMessageBox("Ошибка GetCommState()"); else { dcbPort.BaudRate = CBR_9600; dcbPort.fBinary = TRUE; // двоичный режим обмена dcbPort.fNull = FALSE; // нулевые байты не отбрасываются при передаче dcbPort.Parity = NOPARITY; // бит четности отсутствует dcbPort.ByteSize = 8; // число бит в байте dcbPort.StopBits = ONESTOPBIT;// один стоп-бит int scs = SetCommState(hPort, &dcbPort); if ( scs==0 ) AfxMessageBox("Ошибка SetCommState()"); else { int gct = GetCommTimeouts(hPort, &tm); if ( gct==0 ) AfxMessageBox("Ошибка GetCommTimeouts()"); else { tm.ReadIntervalTimeout = 4; tm.ReadTotalTimeoutConstant = 100; // из диалога настроек порта tm.ReadTotalTimeoutMultiplier = 0; tm.WriteTotalTimeoutConstant = 0; tm.WriteTotalTimeoutMultiplier = 4; int sct = SetCommTimeouts(hPort, &tm); if ( sct==0 ) AfxMessageBox("Ошибка SetCommTimeouts()"); else { dataInPort[0] = 0x01; dataInPort[1] = 0x18; dataInPort[2] = 0x00; dataInPort[3] = 0x00; unsigned short controlSum = 0; controlSum = calcCRC((unsigned char *)dataInPort, 4); dataInPort[4] = (controlSum >> 8); dataInPort[5] = (unsigned char)controlSum; // запись over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if ( over.hEvent == NULL ) AfxMessageBox("Ошибка CreateEvent()"); else { BOOL scm = SetCommMask(hPort, EV_RXCHAR); if ( scm==FALSE ) AfxMessageBox("Ошибка SetCommMask()"); else { dwWrite = 0; dwWait = 0; BOOL wf = WriteFile(hPort, &dataInPort, 6 ,&dwWrite, &over); if ( wf==TRUE ) AfxMessageBox("Ошибка WriteFile()"); else { int wce = WaitCommEvent(hPort, &eventMask, &over); if ( (!wce) && (GetLastError()==ERROR_IO_PENDING) ) { DWORD wfso = WaitForSingleObject(hPort, timeout); switch ( wfso ) { case WAIT_ABANDONED: { // не знаю что это такое AfxMessageBox("WaitForSingleObject: abandon"); } break; case WAIT_OBJECT_0: { // не разобрался для чего это надо AfxMessageBox("че-та происходит"); } break; case WAIT_TIMEOUT: { // считываем данные, если они есть if ( eventMask & EV_RXCHAR ) { dwRead = 0; readed = 0; int cce = ClearCommError(hPort, &errorMask, &state); if ( cce==0 ) AfxMessageBox("Ошибка ClearCommError()"); else { BOOL rf = ReadFile(hPort, &dataFromPort, state.cbInQue, &dwRead, &over); if ( rf==FALSE ) AfxMessageBox("Ошибка ReadFile()"); else { if ( dwRead==0 ) { // правильно? AfxMessageBox("таймаут истек"); } else { // типа тут можно начинать обработку принятых данных? AfxMessageBox("можно начинать обработку данных..."); } } } } else { //где правда ? AfxMessageBox("device not response"); } } break; case WAIT_FAILED: { AfxMessageBox("Ошибка WaitForSingleObject()"); } break; } } else { AfxMessageBox("Ошибка WaitCommEvent()"); } } } } } } } } } } CloseHandle(over.hEvent); CloseHandle(hPort); } протестил вышенаписанное - все работает так как надо, но может есть замечания о некорректности? и мне неясны з МСДН некоторые вещи (в коде означены комментариями) |
|
Сообщ.
#9
,
|
|
|
|
olddiller: Да, c режимом эвента я переборщил
emp: Я тебя пытался подтолкнуть к мысле, что по предоставленному тобой коду, тут вполне мона обойтись без асинхронной возможности работы через оверлаппед интерфейс (тем более если ты не до конца понимаешь как оно работает). У тебя шаги выполняются последовательно, и никаких дополнительных действий (помимо работы с портом) ты не предпринимаешь (реакция прекращения работы потока/приложения/etc). Ну дык и зачем использовать оверлаппед? Сделай проще, синхронно: 1) Открыть порт без оверлаппед 2) Настроить таймауты с учетом цитаты из мсдн, что я приводил выше 3) Вызвать синхронный WriteFile 4) В случае успеха вызвать ReadFile 5) После ReadFile имеешь либо нормальный ответ, либо ошибку чтения. Причины уже отдельно определяешь... 6) Закрываешь порт В случае, если считывать нада порциями, либо нада по самим данным определять конец посылки - в шаги 4-5 вставляешь обработку этого. Возможно по ClearCommError/WaitCommEvent/etc. |
|
Сообщ.
#10
,
|
|
|
|
сделаю небольшие пояснения для чего мне это все надо:
1. во-первых, я хочу разобраться с асинхронным вводом-выводом 2. во-вторых, программа будет работать следующим образом: до завершения работы программы будет крутиться в отдельном потоке вышеприведенный код и по расшифрованным принятым данным (они будут отображаться на далоге) пользователем будут приниматься те или иные действия, в число которых будет приостанавливаться этот постоянный цикл опроса девайса. 3. да и в-третьх, почму асинхронный? да потому, что каждый раз ожидая ответа от девайса я не знаю сколько данных придет (от 12 до 245 байт - в этих пределах) еще раз прошу, лучше помогите разобраться с асинхронным вводом/выводом раз и навсегда ![]() этому желанию и служит же данный форум, правда ? |
|
Сообщ.
#11
,
|
|
|
|
1) Всёж таймауты надоб так:
![]() ![]() tm.ReadTotalTimeoutConstant = 500; // Таймаут в мс на чтение по ReadFile tm.ReadIntervalTimeout = MAXDWORD; // Между байтами может быть какой угодно интервал Ты вроде 500 мс хотел... 2) После успешного WriteFile (без ухода в состояние IO_PENDING) у тебя будет диалог с ошибкой 3) Перепутал WAIT_OBJECT_0 и WAIT_TIMEOUT. WAIT_OBJECT_0 -> Произошло событие на указанном хэндле 4) &dataFromPort - амперсанд тут зачем? 5) То, что ReadFile с параметром оверлаппеда вернула FALSE - ещё не значит что произошла уж такая критичная ошибка. Нада проверить на ERROR_IO_PENDING ЗЫ. После основного вызова асинх. операции проверяй что ты действительно попал в состояние IO_PENDING. И далее можешь делать либо синхронно: ![]() ![]() BOOL fOK = GetOverlappedResult(...., TRUE); или BOOL fOK = WaitForSingleObject(..., timeout) == WAIT_OBJECT_0; либо асинхронно: ![]() ![]() BOOL fOK; while( !(fOK = GetOverlappedResult(..., FALSE)) && GetLastError() == ERROR_IO_INCOMPLETE) { ; // что-то делаешь ещё. } или DWORD dwWaitCode; while( (dwWaitCode = WaitForSingleObject( ..., 5 /* ждать по 5 мс */)) == WAIT_TIMEOUT) { ; // что-то делаешь ещё } Добавлено ЗЫЗЫ. И воспользуйся поиском по совету SVK. Тем дофига было о работе с ком-портами.Может чего интересного подчерпнёшь... |
|
Сообщ.
#12
,
|
|
|
|
я бы топик не создавал если бы тчо то нашел
да и кроме того я вижу что ты тоже не совсем понимаешь... не буду комментррировать что именно топик не закрываю! жду предложений еще народ поднапрягите умы !! |
|
Сообщ.
#13
,
|
|
|
|
Цитата emp @ я бы топик не создавал если бы тчо то нашел да и кроме того я вижу что ты тоже не совсем понимаешь... не буду комментррировать что именно Ооо...Вон оно как...Уж прости, что отнял у тебя драгоценное время. Больше тут писАть не буду. |
|
Сообщ.
#14
,
|
|
|
|
мдя... для всех это такая тяжелая тема?
ну спасибки всем за помощь! Добавлено а может есть кто продвинутый, кто может подсказать как исправить мой код, а не советовать "поищи в инете" или "ты в этом ничего не понимаешь, даже не берись за это"... а я хочу понимать! и мне надо разобраться жду советов... |
|
Сообщ.
#15
,
|
|
|
|
почему нету монстров в етом вопросе ?
|
|
Сообщ.
#16
,
|
|
|
|
Думаю будет интересно почитать, смотри аттач...
Прикреплённый файл WORKComLPT.rar (52.31 Кбайт, скачиваний: 176)
|
|
Сообщ.
#17
,
|
|
|
|
спасибо, ViGOur
Эту статью я читал несколько раз и изначально следовал ей... там все классно написано, но к своему обмену я не могу ее применить почему-то... вот после этого и стал искать помощи на этом форуме щас я в очередной раз изменил свой код, вроде все работает и в принципе можно оставить все как есть, но если б кто-нибудь смог бы мне объяснить некоторые моменты не в стиле Ace, а более по-человечески, то я бы мигом привел бы очередной код здесь с комментариями в неясных мне момента. а синхронный обмен, с которым у меня нет проблем, мне ТОЧНО не подходит, а жаль... ![]() но разобраться мне очень надо. Из всех моих знакомых-программистов в миру, а не в нете, никто не занимается портами так что вживую и спросить не у кого |