Авторизация

Логин: Пароль:
Регистрация Забыли свой пароль?

Программирование Com-порта

Страницы: 1
Программирование Com-порта
Приветик всем! Помогите девушке написать программу на Паскале для работы с Com-портом! надо считать данные из одного приборчика smile:)
С радостью, а чуть-чуть конкретнее можете описать проблему? Хоть что-нибудь в данном направлении сделано. Если ничего не сделано тоже не страшно. Будем решать проблему с нуля. Вы программу на чем пишите Delphi, TurdoPascal или еще в чем-то?
Вот неплохая статья в интернет нашел, может поможет =) + исходник
Вступление

Порядок запуска и работы "службы" (назовем все описываемое ниже так) 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), забывая при этом описать как это делать smile:)
Итак потоки.

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

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

Далее в разделе глобальных переменных определим поток-переменную полученного выше типа

CommThread:TCommThread; //наш поток, в котором будет работать процедура опроса порта

Затем в разделе implementation начинаем ваять.
ВНИМАНИЕ!!!
К этому времени порт уже должен быть инициализирован функцией CreateFile.

1. 1. Инициализируем поток, используя метод Create.

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

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_номер_телефона_интернет_провайдера - мне нравится больше всего smile:)
* ATDP - набор в импульсном режиме, ATDT - набор в тоновом режиме



Да, еще. Проект написан под Delphi3, при использовании Delphi более свежих версий возможны ошибки "несовпадения типов".
В этом случае поменяйте типы "ошибочных" переменных с Integer на Cardinal.
comport.zip (7.17 КБ) [ Скачать ]
Хочется сказать больше, та вот длины TMemo не хватает.
Открытие порта:

HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
Cyber
lpFileName — указатель на строку с нулевым завершающим символом. Обычно это имя открываемого файла, но в нашем случае это должно быть название порта (COM1, COM2, …).

dwDesiredAccess — тип доступа. В нашем случае должен быть равен GENERIC_READ|GENERIC_WRITE.

dwShareMode — параметр совместного доступа. Для коммуникационных портов всегда равен 0.

lpSecurityAttributes — атрибут защиты. Для коммуникационных портов всегда равен NULL.

dwCreationDistribution — режим автосоздания. Для коммуникационных портов всегда равен OPEN_EXESTING.

dwFlagsAndAttributes — атрибут режима обработки. Для коммуникационных портов должен быть равен 0 или FILE_FLAG_OVERLAPPED.

hTemplateFile — описатель файла-шаблона. Для коммуникационных портов должен быть равен NULL.

При успешном открытии порта функция возвращает его описатель, а в случае ошибки возвращает INVALID_HANDLE_VALUE.
Cyber
Это на Delphy
Cyber
На C минус минус smile:)
Страницы: 1
Читают тему (гостей: 1, пользователей: 0, из них скрытых: 0)