Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 01.06.2010, 13:36
osandr osandr вне форума
Прохожий
 
Регистрация: 01.06.2010
Сообщения: 2
Репутация: 10
По умолчанию Асинхронное чтение принятых байтов от COM-порта

Пишу программу на Delphi 7 для управления устройством на микроконтроллере. Устройство подключается к COM-порту. Устройство работает по следующему принципу: программа посылает один или несколько байт, которые устройство расценивает как команды. В зависимости от команды оно посылает соответствующий ответ в виде одного или нескольких байт.
В программе есть два потока. Один – для чтения, второй – для записи в порт. Поток чтения постоянно запущен. Как только приходят байты, происходит их считывание в буфер, представляющий собой TList. Поток записи запускается по необходимости. Мне нужно контролировать принятые байты, чтобы знать, как устройство отреагировало на команды.
Так вот вопрос следующий: как мне заставить программу подождать, пока в списке TList появится первый элемент, т.е. придет хотя бы один байт? В микроконтроллере это реализуется просто. Запускается цикл while, в котором ожидается наступление какого-то события, например установки какого-либо флага. Если использовать такой метод в моей программе, то она будет зависать. Как организовать ожидание прихода байта (или нескольких байт), без зависания программы?
Ответить с цитированием
  #2  
Старый 07.06.2010, 13:37
Grey_p9 Grey_p9 вне форума
Прохожий
 
Регистрация: 07.06.2010
Адрес: Киров
Сообщения: 2
Репутация: 10
По умолчанию

Чтение должно происходить в не в основном потоке программы, а в специально созданном для этого. Тогда зависания окна программы не будет. Что бы поток не грузил проц на 100% нужно его приостанавливать, а пробуждать по приходу байта в порт. делается примерно так:

Код:
procedure TRdThread.Execute;
Var
  ComStat:TComStat;   //состояние порта
  Errs:Cardinal;
  RdOvr:TOverlapped;  //параметры асинхронной операции чтения

begin
  FillChar(RdOvr,SizeOf(TOverlapped),0);// инициализируем структуру TOverlapped
  RdOvr.hEvent:=CreateEvent(nil,  //параметры защиты, если nil, то беруться от родительского процесса
                            true, //режим управления событием
                            false,//начальное состояние false - несигнальное состояние (занят)
                            nil   //имя обекта nil - нет имени
                            );
  SetCommMask(          //задаем события, которые будут отслеживаться портом
              Port,     //дескриптор порта
              //EV_RXCHAR //маска событий EV_RXCHAR-принят байт
              EV_RXFLAG //маска событий EV_RXCHAR-принят байт
              );

  while not Terminated do begin
    WaitCommEvent(  // инициируем ожидание
                  Port,   //дескриптор порта
                  mask,   //маска событий
                  @RdOvr  //указатель на WrOvr
                );
    WaitForSingleObject(RdOvr.hEvent,INFINITE);  //ждем
    GetOverlappedResult(Port,RdOvr,nRead,false); //после этого в переменной mask будет маска того события которое произошло

    ClearCommError(Port,Errs,@ComStat);       //считываем состояние порта
    nToRead:=ComStat.cbInQue;                 //считываем число байт для чтения из структуры
    GetMem(RcBuf,nToRead);
    if not ReadFile(Port,RcBuf^,nToRead,nRead,@RdOvr) //считываем данные
      then Synchronize(UpdateMainFormlabel5)          //Если данные не считались выводим сообщение об ошибке
      else Synchronize(UpdateMainForm);               //Если данные считались выводим их на форму
    FreeMem(RcBuf);                                   //Освободим буфер
  end;
end;

С помощью CreateEvent сначала создаем объект "событие", потом настраиваем COM порт (функция SetCommMask) так что бы при определенных событиях (например прием байта) изменялось состояние объекта "событие", созданного функцией CreateEvent. Потом "связываем" нужный порт с нужным объектом "событием" с помощью функции WaitCommEvent, затем останавливаем поток(WaitForSingleObject), который проснется автоматически как только изменится сотояние "события", а оно меняется при появлении данных.
Вот как то так.
Ответить с цитированием
  #3  
Старый 13.06.2010, 18:44
osandr osandr вне форума
Прохожий
 
Регистрация: 01.06.2010
Сообщения: 2
Репутация: 10
По умолчанию

Спасибо, Grey_p9! Но это у меня уже так и было реализовано. Вопрос был в другом. Я решил эту проблему с помощью Application.ProcessMessages

Теперь вопрос в другом: WaitForSIngleObject ждет одного байта, и буффер заполняется только на один байт и потом снова на один, а как мне сделать, чтобы он заполнялся пачками байтов по 8 штук?
Ответить с цитированием
  #4  
Старый 18.06.2010, 11:31
Grey_p9 Grey_p9 вне форума
Прохожий
 
Регистрация: 07.06.2010
Адрес: Киров
Сообщения: 2
Репутация: 10
По умолчанию

можно ждать не прихода первого байта, а прихода определенного символа, например символа конца строки и в конце каждой посылки из контроллера вставлять этот символ. Для этого в SetCommMask нужно использовать вместо EV_RXCHAR (событие возникает при приеме любого байта) EV_RXFLAG (событие возникает при приеме байта определенного в структуре DCB в поле EvtChar)

Последний раз редактировалось Grey_p9, 18.06.2010 в 11:39.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 15:39.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter