Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Чтение данных из ком-порта в WINXP
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы
skopus
Задача стоит такая:
в устройство отправляется последовательность байт - команда на исполнение. После этого компьютер ждет кода ответа от устройства. Необходимо сделалать так, чтобы пока он ждет, можно было отправить код экстренной остановки, нажав на программную кнопку и, соответственно перестать ждать ответа.

Application->ProcessMessages(); не помогает :(
как же это делается?

нашел несколько статей по поводу того как это делается через потоки на Visual C++ 6.0
но на практике код не переносится, да и в многопоточном программировании у меня никакого опыта нет.
Всякие хитрые компоненты применять запрещено.

Помогите пожалуйста! Скоро уже защита :((

На микроконтроллере я бы сделал отправку байта по внешнему прерыванию от кнопки. Но на программном уровне в виндоус так похоже нельзя, да?
_artem_
rezim overlapped IO ne podxodit ?
Aleks17
Я правда в Билдере нифига не понимаю - на Visual C++ пишу, но думаю принципы те же:

Вариант 1) Многопоточное приложение.

Вариант 2) Открываешь порт для асинхронных операций (FILE_FLAG_OVERLAPPED по-моему)

и организуешь процедуру таймера, внутри которой (а не в процедуре) проверяешь на получение данных.
skopus
Цитата(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++
пробовал как-то на билдер перенести.. нудачно. Ща посмотрю еще раз
_artem_
Eto standartniy API - builder ili visualC ne dolzen imet znacheniya .
one_man_show
Уже писалось не раз: ищешь на torry.net TComPort (автор Dejan Crnila), у него и под Дельфи и под Билдер. Если не хочешь использовать компонент, то просто смотришь исходник, это один из "очень правильно написанных" компонентов для СОМ-порта
skopus
Цитата(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);
             }
}


мне кажется, что я сделал что-то не так...
skopus
Цитата(one_man_show @ May 4 2006, 18:02) *
Уже писалось не раз: ищешь на torry.net TComPort (автор Dejan Crnila), у него и под Дельфи и под Билдер. Если не хочешь использовать компонент, то просто смотришь исходник, это один из "очень правильно написанных" компонентов для СОМ-порта


только исходники на паскале.. а я не знаком с ним абсолютно
Aleks17
Цитата(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 пользуешь, функция же возвращает ссостояние.
skopus
Цитата(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 пользуешь, функция же возвращает ссостояние.


но че-то это так и не заработало. Кнопку остановки так и нельзя нажать, пока программа ждет приема из КОМ-порта.
Ща попробую ожидание приема в отдельный поток вынести
Aleks17
Так ты, сдается мне, открыл порт без надлежащего флага
Old1
Цитата(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);
javalenok
Вообще, потоки для коммуникации нафиг не нужны. Потоки для исполнения процессорных команд нужны. Ведь не зря они ещё виртуальными процессорами называются. Это дибилизм создавать поток, который только и делает, что ждёт окончания ввода-вывода. Более того, если посылать/принимать более одного блока сразу, то 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-компонент не надо.
unichorn
Кстати неплохой пример работы с COM портами есть у Texas Instruments (slaa096b.pdf)
Я на этом примере хорошую DLL для себя в билдере сделал smile.gif


Сам файл прицепить неполучается angry.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.