|
Чтение данных из ком-порта в WINXP, избитая тема, но решения для Builder 6 нигде не нашел |
|
|
|
 |
Ответов
(1 - 13)
|
May 4 2006, 12:33
|

Участник

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124

|
Цитата(Aleks17 @ May 4 2006, 16:24)  Я правда в Билдере нифига не понимаю - на Visual C++ пишу, но думаю принципы те же:
Вариант 1) Многопоточное приложение.
Вариант 2) Открываешь порт для асинхронных операций (FILE_FLAG_OVERLAPPED по-моему)
и организуешь процедуру таймера, внутри которой (а не в процедуре) проверяешь на получение данных. а как тогда организовать многопоточное приложение? сам алгоритм? 1. Отправил строку в порт 2. Создал поток для чтения а в первом потоке будет событие OnClick отрабатываться? Цитата(_artem_ @ May 4 2006, 16:23)  rezim overlapped IO ne podxodit ? возможно и подоходит. Но в Msdn как обычно примеры для Visual C++ пробовал как-то на билдер перенести.. нудачно. Ща посмотрю еще раз
|
|
|
|
|
May 4 2006, 14:07
|

Участник

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124

|
Цитата(Aleks17 @ May 4 2006, 16:24)  Я правда в Билдере нифига не понимаю - на Visual C++ пишу, но думаю принципы те же:
Вариант 1) Многопоточное приложение.
Вариант 2) Открываешь порт для асинхронных операций (FILE_FLAG_OVERLAPPED по-моему)
и организуешь процедуру таймера, внутри которой (а не в процедуре) проверяешь на получение данных. вот посмотрите Код MComPort.Write(&lpBuf,1); // отправили байт в устройство Timer1->Enabled=true; //включили таймер
memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap)); //это для overlap MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //MComPort.Overlap - сама структура оверлап MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer
//обработчик события таймера void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0) { GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE); } else { Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK); } } мне кажется, что я сделал что-то не так...
|
|
|
|
|
May 4 2006, 14:28
|

Участник

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124

|
Цитата(one_man_show @ May 4 2006, 18:02)  Уже писалось не раз: ищешь на torry.net TComPort (автор Dejan Crnila), у него и под Дельфи и под Билдер. Если не хочешь использовать компонент, то просто смотришь исходник, это один из "очень правильно написанных" компонентов для СОМ-порта только исходники на паскале.. а я не знаком с ним абсолютно
|
|
|
|
|
May 5 2006, 05:25
|
Местный
  
Группа: Свой
Сообщений: 238
Регистрация: 17-01-05
Из: Новосибирск
Пользователь №: 2 003

|
Цитата(skopus @ May 4 2006, 21:07)  вот посмотрите Код MComPort.Write(&lpBuf,1); // отправили байт в устройство Timer1->Enabled=true; //включили таймер
memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap)); //это для overlap MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //MComPort.Overlap - сама структура оверлап MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer
//обработчик события таймера void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0) { GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE); } else { Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK); } } мне кажется, что я сделал что-то не так... Ну в целом похоже. Только место для структуры помнится надо задавать до открытия порта (не помню точно). И Event смотреть не обязательно (да и создавать тоже) если GetOverlappedResult пользуешь, функция же возвращает ссостояние.
|
|
|
|
|
May 5 2006, 06:39
|

Участник

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124

|
Цитата(Aleks17 @ May 5 2006, 09:25)  Цитата(skopus @ May 4 2006, 21:07)  вот посмотрите Код MComPort.Write(&lpBuf,1); // отправили байт в устройство Timer1->Enabled=true; //включили таймер
memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap)); //это для overlap MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //MComPort.Overlap - сама структура оверлап MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer
//обработчик события таймера void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0) { GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE); } else { Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK); } } мне кажется, что я сделал что-то не так... Ну в целом похоже. Только место для структуры помнится надо задавать до открытия порта (не помню точно). И Event смотреть не обязательно (да и создавать тоже) если GetOverlappedResult пользуешь, функция же возвращает ссостояние. но че-то это так и не заработало. Кнопку остановки так и нельзя нажать, пока программа ждет приема из КОМ-порта. Ща попробую ожидание приема в отдельный поток вынести
|
|
|
|
|
May 5 2006, 08:14
|

Знающий
   
Группа: Свой
Сообщений: 697
Регистрация: 26-07-05
Из: Могилев
Пользователь №: 7 095

|
Цитата(skopus @ May 5 2006, 09:39)  Цитата(Aleks17 @ May 5 2006, 09:25)  Цитата(skopus @ May 4 2006, 21:07)  вот посмотрите Код MComPort.Write(&lpBuf,1); // отправили байт в устройство Timer1->Enabled=true; //включили таймер
memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap)); //это для overlap MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //MComPort.Overlap - сама структура оверлап MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer
//обработчик события таймера void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0) { GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE); } else { Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK); } } мне кажется, что я сделал что-то не так... Ну в целом похоже. Только место для структуры помнится надо задавать до открытия порта (не помню точно). И Event смотреть не обязательно (да и создавать тоже) если GetOverlappedResult пользуешь, функция же возвращает ссостояние. но че-то это так и не заработало. Кнопку остановки так и нельзя нажать, пока программа ждет приема из КОМ-порта. Ща попробую ожидание приема в отдельный поток вынести Естественно через таймер не заработает, так как процедура-обработчик стоит на месте пока не сработает WaitForSingleObject, а так как таймер работает в главном потоке - то главный поток стоит. Выход - организовать процедуру чтения в отдельном потоке - это позволит управлять главным потоком приложения (нажимать на кнопки), но надо иметь в виду, что пока таймаут у WaitForSingleObject не кончится (если еще байты от устройства не пришли во входной буфер ком-порта) поток чтения все равно не убъешь, только если закрыть приложение. На C++Builder поток реализуется следующим образом: Код DWORD WINAPI ReadBites()//процедура потока { if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0) { GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE); } else { Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK); } }
HANDLE ht=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ReadBites, NULL, 0, &ind);//запускаем поток CloseHandle(ht);
|
|
|
|
|
May 7 2006, 18:58
|
Местный
  
Группа: Участник
Сообщений: 290
Регистрация: 18-02-06
Пользователь №: 14 469

|
Вообще, потоки для коммуникации нафиг не нужны. Потоки для исполнения процессорных команд нужны. Ведь не зря они ещё виртуальными процессорами называются. Это дибилизм создавать поток, который только и делает, что ждёт окончания ввода-вывода. Более того, если посылать/принимать более одного блока сразу, то overlapped надо по-любому, даже для одного потока. Если не хочешь с потоками связываться и рулить вводом-выводом прямо из GUI потока, есть два метода. Опробовано на Дельфи. Метод первый, идентичен модальному диалогу, типа MessageBox. Т.е. процедура не возвращается пока IO не состоиться (либо юзер кликнет cancel). При этом сообщения (отрисовка, нажатия кнопок) доходят до всех окон. Код //Открываешь порт с overlapped: hFile := CreateFile(PChar(OpenDialog1.FileName), GENERIC_READ, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); Win32Check(hFile <> INVALID_HANDLE_VALUE, 'Opening file "' + OpenDialog1.FileName + '"');
try // close file
//Инициализируешь OL структуру: FillChar(ol, SizeOf(ol), 0); ol.hEvent := CreateEvent(nil, true, false, nil); Win32Check(ol.hEvent, 'Creating event');
try // finalize event
можешь ещё кнопки ненужные тут пов(ы)ключать.
//зачинаешь запись: Win32Check(WriteFile(hFile, buf, Size, Size, @ol)or (GetLastError() = ERROR_IO_PENDING), 'ReadFile');
ещё можно зафлашить выходной буффер, на всякий случай.
и ждёшь окончания в цикле:
repeat
dw := MsgWaitForMultipleObjects(1, ol.hEvent, False, INFINITE, QS_ALLINPUT);
Win32Check(dw <> $FFFFFFFF, 'MsgWaitForMultipleObjects returns ' + IntToStr(Size));
if dw = WAIT_OBJECT_0 then begin
// get number of bytes read Win32Check(GetOverlappedResult(hFile, ol, dw, False), 'GetOverlappedResult'); //ol.Offset := ol.Offset + Size; if dw <> Size then raise Exception.Create('Written ' + IntToStr(dw) + ' instead of ' + IntToStr(Size)) else Break; // а можно следующий блок данных в порт вывести или перейти в режим чтения end;
// если мы тут, то выход из ожидания произощёл из-за сообщения в очереди потока Application.ProcessMessages;
until false;
finally
// разрешить кнопки
CloseHandle(ol.hEvent);
end; finally CloseHandle(hfile); end; Если теперь надо послать ещё одну команду (блок данных), она встанет в очередь драйвера порта. OverlappedIO нужно применять в любом случае, дабы не блокировать поток, обслуживающий пользовательский интерфейс. Из описания проблемы я не понял, почему следущюю команду нужно посылать до окончания предидущей, отчего такая большая задержка? Команда длинная, устройство не быстрое? Если из-за длинной команды, выж понимаете, что из-за очереди в драйвере, ваше "прерывание" дойдёт только после первого блока. Тут может потребываться разрыв связи (closefile), перед отсылкой новой команды. Другой подход - асинхронные процедуры. Создаёшь файл и OL точно так же. Инициацию ввода вывода снабжаешь указателем на Код procedure FileIOCompletionRoutine(dwErrorCode: DWORD; dwNumberOfBytesTransfered: DWORD; lpOverlapp: POVERLAPPED) stdcall; Тут сам соорентируешься, что в ней делать. Когда всё записано/прочитано не забудь порт и событие прикрыть. Трудность этого метода в том, что цикл обработки сообщений Дельфи, реализованный в модуле Forms.TApplication.Idle(Msg), использует для ожидания процедуру WaitMessage. Это значит, что от выходит из неё, когда приходит сообщение, игнорируя окончания ВВ. А нам нужно, чтобы запенить её на MsgWaitForMultipleObjectsEx: Код Done := False; // if Done then WaitMessage; if Done then MsgWaitForMultipleObjectsEx(0, Control {fake parameter}, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE); Перекрыть метод по ООП-шному не удалось, пришлось "хакать" по-гразному: делать локальную копию системного модуля Forms. Вообще эти методы OverlappedIO всем хороши, еслибы билли и тут не напакостил. Если дрыгать окно мышой, или ресайзить управление переходит модальной процедуре виндов, реализованной по типу нашего первого метода. Поэтому, пока пользователь не отпустит мышку, управление будет бегать в системном цикле, а не в нашем, не давая нам возможности проверить OverlappedResult ВВ. Более того, эта нелепая системная процедура виндов, как и борландовская, по какой-то непонятной причине ожидает сообщений (отпусти мышку) через WaitForMessage, вместо MsgWaitForMultipleObjectsEx. Поэтому, асинхронные IOCompletion события до нас также не доходят. Полагаю, эта та самая причина, по которой Windows Explorer приостанавливает копирование файлов, если прогресс-бар схватить мышой. То есть, окна рисуются, кнопки реагируют, а IO не идёт. Если это терпимо, то можно использовать. Я же, когда прогу на замену галимому Windows-терминалу делал, отдельный многобуфферный IO-поток по этой причине создавал и обменивался с ним сообщениями-буфферами. Могу выслать, если интересно. И главное, никаких левых T-компонент не надо.
|
|
|
|
|
May 8 2006, 10:33
|
Частый гость
 
Группа: Участник
Сообщений: 96
Регистрация: 24-09-05
Пользователь №: 8 901

|
Кстати неплохой пример работы с COM портами есть у Texas Instruments (slaa096b.pdf) Я на этом примере хорошую DLL для себя в билдере сделал  Сам файл прицепить неполучается
Сообщение отредактировал unichorn - May 8 2006, 10:31
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|