Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подземелье Магов
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  17:07[Войти] | [Зарегистрироваться]

Асинхронный режим чтения из Com-порта

Александр Терехов
дата публикации 06-12-2002 14:58

Асинхронный режим чтения из Com-порта

Вступление

Порядок запуска и работы "службы" (назовем все описываемое ниже так) Com-портов состоит из нескольких достаточно хорошо описанных шагов ( см. статьи по теме ):
  1. Инициализация Com-порта посредством вызова функции CreateFile.
  2. Установка параметров Com-порта посредством последовательного вызова функций GetCommState и SetCommState, а также SetupComm.
  3. Установка параметров тайм-аутов для чтения и записи - GetCommTimeouts и SetCommTimeouts.
  4. Собственно записи в Com-порт - WriteFile и чтения из него - ReadFile.
  5. Закрытие порта по окончанию работ CloseHandle.
Очень большой сложности описанные выше шаги не представляют, однако реализация чтения данных из порта в асинхронном (неблокирующем) режиме заставляет почесать затылок. Об этом и поговорим.

Чтение из Com-порта.

Судя по контексту справки, касающейся функции CreateFile, для "отлова" момента поступления данных в Com-порт следует использовать функцию WaitCommEvent. Предварительно установив маску SetCommMask на то событие, которое хотелось бы отследить. Нужное событие наступает - вызываем функцию ReadFile для чтения поступающих данных.

Казалось бы все в порядке, но... Вызов функции WaitCommEvent насмерть тормозит приложение, пока какие-либо данные не поступят в Com-порт.

Можно конечно, просто взять и запустить в непрерывном цикле ReadFile, однако приложение хотя и будет как-то шевелиться, но это шевеление скорее всего будет напоминать предсмертные судороги.

Как выход из ситуации многие предлагают использовать потоки (thread), забывая при этом описать как это делать :)

Итак потоки.

В модуле Classes для потоков определен специальный класс TThread. Для создания потоков специалисты рекомендуют использовать именно его, а не создавать потоки используя BeginThread и EndThread, т.к. библиотека VCL не является защищенной для потоков в такой реализации. Следуя советам экспертов, для организации контроля поступающих данных в Com-порт и будем использовать готовый класс TThread.

В раздел interface определим тип переменных этого класса, переопределив только один метод класса - Execute, ну и дополнительно объявим свой метод, который и займется опросом Com-порта.

Type
  //определим тип TComThread - наследника класса TThread
  TCommThread = class(TThread)
  private
  //процедура, занимающаяся опросом порта
    Procedure QueryPort;
  protected
  //переопределим метод запуска потока
     Procedure Execute; override;
end;

Далее в разделе глобальных переменных определим поток-переменную полученного выше типа
CommThread:TCommThread; 
//наш поток, в котором будет работать процедура опроса порта
Затем в разделе implementation начинаем ваять.
ВНИМАНИЕ!!!
К этому времени порт уже должен быть инициализирован функцией CreateFile.
  1. 1. Инициализируем поток, используя метод Create.

    Procedure StartComThread;
    //инициализация нашего потока
    Begin {StartComThread}
    	//пытаемся инициализировать поток
    	CommThread:=TCommThread.Create(False);
    	//проверяем получилось или нет
    	If CommThread = Nil Then
    	Begin {Nil}
    		//ошибка, все выключаем и выходим
    		SysErrorMessage(GetLastError);
    		fmMain.btnStop.Click;
    		Exit;
    	End; {Nil}
    End; {StartComThread}
    

    Куски кода взяты из файла проекта, поэтому нажимание на кнопку btnStop главной формы fmMain - это "примочки" примера, не обращайте внимания.

  2. Запускаем процедуру опроса порта в нашем потоке.

    Procedure TCommThread.Execute;
    Begin {Execute}
    	Repeat
    		QueryPort;
    		//процедура опроса порта будет производиться 
    		//пока поток не будет прекращен
    	Until Terminated;
    End;  {Execute}
    

  3. Реализуем асинхронные опрос порта и чтение из него данных

    Procedure TCommThread.QueryPort;
    Var
    	MyBuff:Array[0..1023] Of Char;//буфер для чтения данных
    	ByteReaded:Integer; //количество считанных байт
    	Str:String;         //вспомогательная строка
    	Status:DWord;       //статус устройства (модема)
    Begin {QueryPort}
    //получим статус COM-порта устройства (модема)
    	If Not GetCommModemStatus(hPort,Status) Then
    	Begin {ошибка при получении статуса модема}
    	//ошибка, все выключаем и выходим
    		SysErrorMessage(GetLastError);
    		fmMain.btnStop.Click;
    		Exit;
    	End;  {ошибка при получении статуса модема}
    	//Обработаем статус устройства (модема) и будем 
            //включать(выключать) лампочки
    	//готовность устройства (модема) получать данные
    	fmMain.imgCTSOn.Visible:=((Status AND MS_CTS_ON)=MS_CTS_ON);
    	//готовность устройства (модема) к сеансу связи
    	fmMain.imgDSROn.Visible:=((Status AND MS_DSR_ON)=MS_DSR_ON);
    	//принимаются данные с линии сигнала
    	fmMain.imgRLSDOn.Visible:=((Status AND MS_RLSD_ON)=MS_RLSD_ON);
    	//входящий звонок
    	fmMain.imgRingOn.Visible:=((Status AND MS_RING_ON)=MS_RING_ON);
    
    	//читаем буфер из Com-порта
    	FillChar(MyBuff,SizeOf(MyBuff),#0);
    	If Not ReadFile(hPort,MyBuff,SizeOf(MyBuff),ByteReaded,Nil) Then
    	Begin {ошибка при чтении данных}
    		//ошибка, все закрываем и уходим
    		SysErrorMessage(GetLastError);
    		fmMain.btnStop.Click;
    		Exit;
    	End; {ошибка при чтении данных}
    	//данные пришли
    	If ByteReaded>0 Then
    	Begin {ByteReaded>0}
    		//посчитаем общее количество прочитанных байтов
    		ReciveBytes:=ReciveBytes+ByteReaded;
    		//преобразуем массив в строку
    		Str:=String(MyBuff);
    		//отправим строку на просмотр
    		fmMain.Memo1.Text:=fmMain.Memo1.Text+ Str;
    		//покажем количество считанных байтов
    		fmMain.lbRecv.Caption:='recv: '+IntToStr(ReciveBytes)
    			+' bytes...';
    	End; {ByteReaded>0}
    End; {QueryPort}
    
    

На этом по поводу использования потоков для считывания данных из Com-порта, пожалуй, все.

Прилагающийся пример

Следуя правилам хорошего тона, прикладываю ко всему написанному работающий пример.
В примере используется самое доступное устройство для пользователей интернет - модем (на Com-порту). В качестве "примочек" я использовал лампочки, которые включаются (или выключаются) при изменении статуса модема. Можно было прикрутить лампочки-детекторы входящих-выходящих сигналов, но вместо них используются счетчики байтов.
Реализация кода включения-выключения не самая лучшая: можно было бы использовать TImageList для хранения изображений лампочек. Но почему-то ??? (кто знает почему - напишите) использование ImageList.GetBitmap при наличии запущенного потока "подвешивает" приложение насмерть. Причем это происходит под Windows'98, если тоже самое делать под Windows'95, то все в порядке.

Для проверки работоспособности примера попробуйте понабирать AT-команды
  • ATZ - инициализировать модем
  • ATH - положить трубку
  • ATH1 - поднять трубку
  • ATS0=1 - включить автоподнятие трубки на первый сигнал
  • ATS0=0 - выключить автоподнятие трубки
  • ATDP_номер_телефона_интернет_провайдера - мне нравится больше всего :)
  • ATDP - набор в импульсном режиме, ATDT - набор в тоновом режиме


Да, еще. Проект написан под Delphi3, при использовании Delphi более свежих версий возможны ошибки "несовпадения типов".
В этом случае поменяйте типы "ошибочных" переменных с Integer на Cardinal.

Скачать проект — ComPort.zip (17K)
архив обновлен автором 10.12.02 (добавлена функция обработки кодов ошибок с переводом их значений на русский язык)

Терехов Александр
декабрь 2002г.
Другие небольшие статьи,
примеры и программы можете найти на сайте автора




Смотрите также материалы по темам:
[Работа с модемом] [COM-порт]

 Обсуждение материала [ 06-02-2013 07:28 ] 11 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования