Введение
Delphi — это мощный язык программирования, позволяющий разработчикам с легкостью создавать надежные приложения. Что касается работы с COM-портами, Delphi предоставляет простой способ связи с внешними устройствами. Не следует считать, что COM порт — это нечто безнадежно устаревшее и архаичное. Конечно, вы уже не встретите его в современных гаджетах общего назначения, таких как принтеры, сканеры и т.д. Однако COM порт широко используется до сих пор в промышленной технике, в различных световых рекламных табло. Также без работы с этим портом не обойтись если вы разрабатываете программы для микроконтроллеров, в частности для Arduino.
В этой статье мы рассмотрим, как работать с COM-портами в Delphi, не полагаясь на дополнительные компоненты, используя функции Windows API. Мы рассмотрим основные задачи, такие как открытие COM-порта, настройка его параметров и чтение/запись данных.
Для начала нам нужно открыть COM-порт. Для этого Delphi предоставляет функцию CreateFile из Windows API. Вот пример того, как открыть COM-порт:
var
hPort: THandle;
comPort: string;
begin
comPort := 'COM1'; // Укажем COM-порт, который вы хотите открыть
hPort := CreateFile(
PChar('\\.\' + comPort),
GENERIC_READ or GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0
);
if hPort <> INVALID_HANDLE_VALUE then
begin
// COM-порт успешно открыт
// Выполнить дальнейшие операции...
end
else
begin
// Не удалось открыть COM-порт
ShowMessage('Failed to open COM port ' + comPort);
end;
end;
Настройка параметров COM-порта
После открытия COM-порта нам необходимо настроить его параметры, такие как скорость передачи данных, четность, стоповые биты и т. д. Для этой цели Delphi предоставляет структуру DCB (блок управления устройством) и функции GetCommState/SetCommState из Windows API. Вот пример того, как настроить параметры COM-порта:
var
dcb: TDCB;
begin
// Получить текущие настройки
GetCommState(hPort, dcb);
// Configure the desired settings
dcb.BaudRate := CBR_9600; // Установите скорость передачи данных = 9600
dcb.Parity := NOPARITY; // No parity bit
dcb.ByteSize := 8; // 8 data bits
dcb.StopBits := ONESTOPBIT; // 1 stop bit
// Примените новые настройки
SetCommState(hPort, dcb);
// Дальнейшие операции...
end;
Чтение и запись данных
Теперь, когда мы открыли COM-порт и настроили его параметры, мы можем приступить к чтению и записи данных на подключенное устройство. Для этих операций используются функции ReadFile и WriteFile из Windows API. Вот пример того, как читать и записывать данные:
var
bytesRead, bytesWritten: DWORD;
buffer: array[0..255] of Byte;
begin
// Чтение данных с COM-порта
ReadFile(hPort, buffer, SizeOf(buffer), bytesRead, nil);
// Обработать полученные данные
if bytesRead > 0 then
begin
// Данные успешно получены
// Доступ к полученным данным в буферном массиве
end;
// Запись данных в COM-порт
buffer[0] := $41; // Example: Writing 'A' to the port
WriteFile(hPort, buffer, 1, bytesWritten, nil);
// Проверяем, прошла ли операция записи успешно
if bytesWritten = 1 then
begin
// Данные успешно записаны
end;
// Дальнейшие операции...
end;
Заключение
В этой статье мы рассмотрели, как работать с COM-портами в Delphi, не полагаясь на дополнительные компоненты, используя функции Windows API. Мы рассмотрели открытие COM-порта, настройку его параметров и чтение/запись данных. Понимая и используя функции Windows API, вы получаете полный контроль над процессом связи COM-порта в ваших приложениях Delphi. Экспериментируйте, исследуйте и создавайте удивительные приложения, которые беспрепятственно взаимодействуют с внешними устройствами через COM-порты!
Читать также:
Работа с COM-PORT в C#…
Работа с COM-PORT в Python…
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Продолжаем разбираться с Arduino в рамках «выходного» цикла статей #МастеримДома. Итак, на сегодняшний день у нас имеется устройство на платформе Arduino Uno для измерения концентрации CO2 в помещении и, соответственно, небольшой скетч для преобразования сигнала с датчика MQ135 в эту самую концентрацию. А сегодня мы перейдем непосредственно к теме управления устройством Arduino с помощью программы, написанной в Delphi 10.3 Rio.
Если Вы уже ознакомились со всеми предыдущими статьями (раз, два, три), то вам должно уже быть понятно в чем заключается суть управления Arduino из какой-либо сторонней программы, написанной вообще на любом языке будь то Delphi, Python или любой другой язык. Суть работы будет всегда одна — научиться читать и отправлять данные по средствам COM-порта (RS-232).
Таким образом, сегодня мы сделаем следующее:
- Напишем небольшую программку в Delphi, которая будет читать данные с COM-порта и отправлять команды для Arduino
- Доработаем наш скетч для того Arduino «умела» принимать и обрабатывать отправленные ей команды.
Работа с COM-портом в Delphi
Конечно, было бы намного интереснее (с точки зрения обучения программированию) написать свои собственные методы работы с COM-портом, расписать то, как работать с этими портами и так далее, но я решил просто воспользоваться уже готовыми решениями для Delphi коих тысячи.
В качестве основных компонентов для работы с COM-портами в Delphi я выбрал библиотеку ComPort Library. Несмотря на то, что эта библиотека не обновлялась довольно давно, версия для Delphi XE вполне спокойно скомплировалась и установилась в Delphi 10.3.
После установки на панели компонентов появится новая вкладка CPortLib, содержащая следующие компоненты для работы с RS-232 в Delphi:
Теперь, прежде, чем приступать к разработке приложения, определимся с алгоритмом работы. Итак, наша программа будет:
- Считывать числовые значения из COM-порта. Здесь в качестве числовых значений я подразумеваю, во-первых, значение концентрации в ppm и, во-вторых, калибровочные данные с датчика (R0).
- Отсылать Arduino следующие команды:
- «1» — начать передачу калибровочных данных
- «2» — прекратить передачу всех данных
- «любой символ» — начать или возобновить передачу данных о концентрации
Учитывая то, что, используя только COM-порт для работы с Arduino, мы не можем вносить изменения в скетч и, тем более, его перезагружать в Arduino, в перспективе можно будет продумать вариант получения «сырых» данных с Arduino и их обработку в Delphi, что, на мой взгляд, может облегчить работу с датчиком и избавит от ковыряния в исходниках библиотек для Arduino. Но это только в перспективе.
Итак, интерфейс программы будет вот таким:
На форме расположена кнопка для настройки и подключения к COM-порту, список (TComboBox) для выбора одной из трех команд, описанных выше, метки (TLabel) для вывода среднего значения передаваемой величины и количества передач, Memo для вывода всего потока данных и, собственно, сам компонент TComPort для работы с портом.
Код события OnClick для кнопки подключения следующий:
procedure TfrmMain.Button1Click(Sender: TObject); begin ArduinoCom.ShowSetupDialog;//вызываем диалог настройки порта ArduinoCom.Open; //пробуем открыть порт if ArduinoCom.Connected then lbConnect.Caption:='Подключен на '+ArduinoCom.Port else lbConnect.Caption:='Не подключен'; end;
Сам клик по кнопке вызовет вот такой диалог настройки для компонента (форма диалога входит в библиотеку):
После того, как подключение к порту прошло успешно мы можем считывать данные и проводить необходимые вычисления. Для этого в секции private формы я объявил две переменные:
private aCount: integer; aAvg: double;
aCount — количество измерений; aAvg — среднее значение измеряемой величины.
У компонента TComPort пишем вот такой обработчик события OnRxChar:
procedure TfrmMain.ArduinoComRxChar(Sender: TObject; Count: Integer); var Str: string; aNext: double; begin ArduinoCom.ReadStr(Str, Count);//читаем строку от Arduino memData.Text:=memData.Text+Str;//записываем строку в Memo aNext:=StrToFloat(Trim(str),FS);//полученное числовое значение if aCount=0 then //если это первое значение - оно же и будет средним aAvg:=aNext else //иначе рассчитываем среднее значение aAvg:=aAvg*(aCount/(aCount+1))+aNext/(aCount+1); inc(aCount);//наращиваем счётчик UpdateForm;//обновляем метки на форме end;
Процедура UpdateForm простая:
procedure TfrmMain.UpdateForm; begin lbAvg.Caption:=FloatToStrF(aAvg, fffixed, 6,2); lbCount.Caption:=IntToStr(aCount); end;
Соответственно, при выборе значения в ComboBox мы должны обнулить все значения, отправить Arduino команду и начать отсчёт заново:
procedure TfrmMain.cbCommandChange(Sender: TObject); begin //отправляем команду Arduino case cbCommand.ItemIndex of 0:ArduinoCom.WriteStr('3');//ppm 1:ArduinoCom.WriteStr('1');//ro 2:ArduinoCom.WriteStr('2');//---- end; memData.Text:=EmptyStr; aCount:=0; aAvg:=0; UpdateForm; end;
А при запуске программы не забыть выставить первоначальные значения всех переменных:
procedure TfrmMain.FormCreate(Sender: TObject); begin aAvg:=0; aCount:=0; FS.DecimalSeparator:='.'; UpdateForm; end;
В принципе, на сегодня это и всё, что нам необходимо, чтобы научиться управлять Arduino из Delphi. Теперь перейдем к скетчу Arduino и научим наше устройство «понимать» отправленные ей команды.
Скетч Arduino
Как я говорил, ещё в первой статье посвященной Arduino и Delphi, необходимо чуть-чуть разобраться с С++. Я не скажу, что представленный ниже скетч — это идеальный код (уверен, что можно всё сделать короче и понятнее), но скетч работает и даёт ожидаемый результат. Итак, код скетча для Arduino следующий:
#include <MQ135.h>//подключаем библиотеку для работы с MQ135 #define analogPin A0 // аналоговый выход MQ135 подключен к пину A0 Arduino MQ135 gasSensor = MQ135(analogPin); bool Zero = false; //если True - с датчика читается значение Ro bool Conc = true; //если True - с датчика читается концентрация float Value = 0; //значение с датчика void setup() { Serial.setTimeout(1000); //настраиваем тай-маут для порта Serial.begin(9600); // инициализация последовательного порта pinMode(analogPin, INPUT); // режим работы аналогового пина delay(1000); // даём датчику небольшое время на разогрев } void loop() { if (Serial.available() > 0) { //если есть доступные данные // считываем строку String incomingStr = Serial.readString(); //пробуем преобразовать строку в число int Command = incomingStr.toInt(); switch (Command) { //выбираем, что передавать в порт и передавать ли вообще case 1: Zero = true; Conc = false; break; case 2: Zero = false; Conc = false; break; default: Zero = false; Conc = true; } } if (Zero) Value = gasSensor.getRZero(); // чтение R0 else if (Conc) Value = gasSensor.getPPM(); //чтение концентрации if (Zero || Conc) //стоит ли что-то передавать Serial.println(Value); // выдача в последовательный порт delay(1000); // задержка перед выводом следующего значения }
В принципе, по комментариям в коде должна стать понятной логика скетча, а именно:
1. По умолчанию стартуем с чтением концентрации
2. Если в порт пришло что-либо, то пробуем прочитать строку
3. Если строка представляет собой число 1 или 2, то либо передаем R0, либо прерываем какую-либо передачу вообще
4. Если пришла непонятная строка или, как в случае с нашей программой в Delphi — значение 3, то начинаем передавать значение концентрации.
Теперь можно загрузить скетч в Arduino, запустить нашу Delphi-программку и посмотреть на её работу.
Результат
Передача данных о концентрации CO2 в помещении
Аналогичным образом передаются калибровочные данные:
Таким образом, можно констатировать, что управлять Arduino с помощью программ, написанных в Delphi вполне возможно и делается это относительно просто. Более того, если необходимо, то на «плечи» Delphi можно перенести и все необходимые расчёты (всё же на борту Arduino не так уж и много памяти).
5
4
голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Работа с COM-портами в Delphi.
Вопрос «как работать с COM-портами?» стал классическим на многих конференциях
по языкам программирования. Рано или поздно чуть не каждому программисту
приходится работать с портами ввода/вывода. Сегодня я хочу рассказать
про работу с последовательным портом из-под самой распространенной на
сегодняшний день 32-разрядной операционной системы — Windows. Статья построена
по принципу «от простого к сложному». Сначала будут изложены основы работы
с портами из-под Win32 с описанием необходимых функций. Затем рассмотрим
применение этих функций на примере Delphi-программы. Конечным результатом
будет класс, предназначенный для работы с COM-портом, и пример использующей
его программы.
По ссылке Serial Net Tools — находится написанная мной программа для работы с COM-портом.
Очень часто программисту приходится управлять с помощью компьютера
каким-либо внешним устройством, или просто анализировать состояние этого
устройства. Порты ввода/вывода — самый распространенный способ сопряжения
компьютера и внешнего устройства. Давным-давно уже написано множество
классов, библиотек и компонент для работы с портами, поэтому можно,
конечно, воспользоваться уже готовым и к тому же бесплатным решением.
Именно так я и поступил лет семь назад, при этом потеряв самое главное
— своевременное понимание того, как все-таки работать с портами из-под
Win32. Незнание внутренних механизмов — это, во-первых, пробел в стройном
ряду знаний, а во-вторых, актуальная возможность ошибок в работе программы.
С портами из-под Win32 работают так же, как и с обычными файлами, используя
при этом всего несколько специфичных функций WinAPI. Однако коммуникационный
порт — это не совсем обычный файл. Для него, например, нельзя выполнить
позиционирование файлового указателя, или же создать порт, если таковой
отсутствует. Любая работа с портом начинается с его открытия. Для этого
используется файловая функция WinAPI (описания WinAPI-функций взяты
из MSDN (Microsoft Developer Network), следовательно, приводятся в синтаксисе
C):
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);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.
Сразу оговорюсь: все недостающие описания можно найти на http://msdn.microsoft.comи
еще по ряду адресов, которые вам подскажет поисковый сервер.
Из всех параметров функции CreateFile() особого пояснения требует dwFlagsAndAttributes.
Работа с портом может быть организована в синхронном (nonoverlapped)
или асинхронном (overlapped) режимах обработки, что и задается этим
флагом. При синхронном режиме (когда параметр dwFlagsAndAttributes =
0) только один поток приложения может либо читать, либо писать в порт.
Помните переговорное устройство в лифте? Нажали кнопку — можем только
говорить, отпустили кнопку — можем только слушать.
Синхронный режим обработки прост в реализации. Если надо записать данные
в порт, то вызываем функцию записи и ожидаем, пока она не завершится.
Если же надо читать данные, то вызываем функцию чтения и ждем, пока
она не отработает. Для простых задач синхронный режим обработки вполне
подходит, однако в мире Windows он почти всегда обречен на неудачу.
Ожидание операции чтения или записи воспринимается пользователем программы
как «зависание».
Асинхронный режим (когда параметр dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED)
позволяет производить операции чтения и записи в порт параллельно из
разных потоков. В то время, пока один поток приложения принимает данные,
другой поток может параллельно с первым передавать данные — как при
разговоре по телефону, когда вы можете слушать и говорить одновременно.
Данный режим обработки больше импонирует идее многозадачности Windows.
Но за все надо платить: для реализации этого режима обработки требуется
в два раза больше написанного кода, вдобавок, умения писать многопоточные
программы. Какой режим выбрать — решайте сами. Но если уж разбираться
в работе порта, то разбираться «по-взрослому», до конца, а потому и
рассмотрим более сложный вариант — асинхронную обработку.
На практике открытие порта для асинхронного режима обработки из программы
на Delphi выглядит примерно так:
hPort := CreateFile(‘COM1’, GENERIC_READ or GENERIC_WRITE, 0, nil,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if hPort = INVALID_HANDLE_VALUE then
raise Exception.Create(‘Error opening port’);Функция возвращает описатель
порта (hPort), который нам потом пригодится для вызова других функций
работы с портом. Если в результате открытия порта описатель не получен,
то возбуждается исключение с соответствующим текстом ошибки. Открыв
порт, мы получаем его в свое распоряжение. Теперь с этим портом может
работать только наша программа (точнее, только наш процесс). По окончании
работы с портом его следует закрыть, вызвав функцию:
BOOL CloseHandle(
HANDLE hObject
);В качестве единственного параметра надо передать полученный ранее
описатель порта (hPort).
Хоть система при завершении выполнения программы и освобождает все
выделенные ей ресурсы (в том числе и порты), хорошим тоном программирования
считается собственноручное закрытие портов. Открывать/закрывать порт
как будто несложно. Кроме того, нам потребуется программная настройка
порта. Думаю, все видели диалог настройки последовательного порта в
диспетчере устройств системы. Все эти настройки мы можем произвести
программно. Для этих целей используется функция WinAPI:
BOOL SetCommState(
HANDLE hFile,
LPDCB lpDCB
);hFile — описатель открытого порта.
lpDCB — указатель на структуру DCB.
Основные параметры последовательного порта описываются структурой DCB.
Она содержит массу полей, каждое из которых соответствует определенному
параметру настройки порта. Мы рассмотрим несколько полей, которые нам
нужны:
BaudRate — скорость передачи данных. Возможно указание констант —CBR_100,
CBR_300, CBR_600, CBR_1200, …, CBR_256000.
Parity — схема контроля четности. Может содержать одно из следующих
значений: EVENPARITY, MARKPARITY, NOPARITY, ODDPARITY, SPACEPARITY.
ByteSize — число информационных бит в передаваемых и принимаемых байтах.
StopBits — количество стоповых бит. Может быть ONESTOPBIT, ONE5STOPBIT,
TWOSTOPBIT.
Чтобы не заполнять структуру DCB вручную, ее можно заполнить информацией
о текущем состоянии порта вызовом функции GetCommState(), затем изменить
необходимые поля и установить настройки вызовом функции SetCommState().
Настройку порта желательно производить сразу после его открытия. На
Delphi это выглядит так:
var
Dcb: TDcb;
…
if not GetCommState(hPort, Dcb) then
raise Exception.Create(‘Error setting port state’);
Dcb.BaudRate := CBR_9600;
Dcb.Parity := NOPARITY;
Dcb.ByteSize := 8;
Dcb.StopBits := ONESTOPBIT;
if not SetCommState(hPort, Dcb) then
raise Exception.Create(‘Error setting port state’);
Еще одна операция, которая нам понадобится сразу после открытия порта
— его сброс.
BOOL PurgeComm(
HANDLE hFile,
DWORD dwFlags
);Вызов этой функции очищает очередь приема/передачи и завершает все
находящиеся в ожидании запросы ввода/вывода.
hFile — описатель открытого порта.
dwFlags — производимые действия в виде набора флагов PURGE_TXABORT,
PURGE_RXABORT, PURGE_TXCLEAR, PURGE_RXCLEAR.
Пример на Delphi:
if not PurgeComm(hPort, PURGE_TXCLEAR or PURGE_RXCLEAR) then
raise Exception.Create(‘Error purging port’);На этом подготовительная
фаза заканчивается, и можно приступать непосредственно к приему/передаче
данных. Прием данных у нас будет происходить по событийной схеме; программа
будет ожидать прием одного или нескольких символов (байт). Для перевода
порта в этот режим необходимо вызвать функцию SetCommMask() с флагом
EV_RXCHAR:
if not SetCommMask(hPort, EV_RXCHAR) then
raise Exception.Create(‘Error setting port mask’);Прием и передача данных
выполняется функциями ReadFile() и WriteFile(), то есть теми же самыми
функциями, которые используются для работы с дисковыми файлами. Вот
их описание:
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
hFile — описатель открытого порта.
lpBuffer — адрес буфера.
nNumberOfBytesToRead/nNumberOfBytesToWrite — число ожидаемых к приему
или предназначенных для передачи байт.
lpNumberOfBytesRead/lpNumberOfBytesWritten — число фактически принятых
или переданных байт.
lpOverlapped — адрес структуры OVERLAPPED, используемой для асинхронных
операций.
Передача данных является довольно быстрой операцией, поэтому как правило
ее выполняют из главного потока приложения. На Delphi это выглядит так:
var
dwWrite: DWORD;
OverWrite: TOverlapped;
WriteBytes: array of Byte;
…
begin
OverWrite.hEvent := CreateEvent(nil, True, False, nil);
if OverWrite.hEvent = Null then
raise Exception.Create(‘Error creating write event’);
…
if (not WriteFile(hPort, WriteBytes, SizeOf(WriteBytes),
dwWrite, @OverWrite))
and (GetLastError <> ERROR_IO_PENDING) then
raise Exception.Create(‘Error writing port’);
end;В данном примере функция WriteFile() выполняет асинхронную запись
массива байтов WriteBytes в порт. Она сразу возвращает управление, и
запись в порт происходит параллельно с выполнением основного кода потока.
Если результат WriteFile() равен False, то это значит, что на момент
возврата управления передача массива байтов еще не закончилась. Поэтому
код ошибки выполнения WriteFile() в данном случае должен быть равен
ERROR_IO_PENDING. Переменная OverWrite — overlapped-структура, необходимая
для асинхронных операций.
В принципе, вас не должно волновать, когда закончится передача массива
байтов. Зато момент приема одного или нескольких символов действительно
важен. Поэтому его можно разбить на две части: инициирование приема
и определение момента приема с последующим чтением символов. Поскольку
при этом приходится считаться с фактором ожидания приема символа, рекомендуется
функции приема данных вынести в отдельный поток. Передавать данные можно
и из основного потока приложения, поскольку это происходит довольно
быстро. А вот событие приема символа будем ожидать в отдельном потоке.
Рассмотрение работы с потоками в Windows, в частности того, как это
реализовано в Delphi, выходит за рамки данной статьи. Предполагаю, что
читатель встречался или по крайней мере знаком с этим. Скажу лишь, что
у любого потока есть главная функция, которая начинает выполняться после
его создания. В Delphi для потоков существует класс TThread, а его главная
процедура называется TThread.Execute().
Вот так выглядит главная процедура отдельного потока, которая ожидает
появление одного или нескольких символов и считывает их:
procedure TReadThread.Execute;
var
ComStat: TComStat;
dwMask, dwError: DWORD;
OverRead: TOverlapped;
Buf: array[0..$FF] of Byte;
dwRead: DWORD;
begin
OverRead.hEvent := CreateEvent(nil, True, False, nil);
if OverRead.hEvent = Null then
raise Exception.Create(‘Error creating read event’);
FreeOnTerminate := True;
while not Terminated do
begin
if not WaitCommEvent(hPort, dwMask, @OverRead) then
begin
if GetLastError = ERROR_IO_PENDING then
WaitForSingleObject(OverRead.hEvent, INFINITE)
else
raise Exception.Create(‘Error waiting port event’);
end;
if not ClearCommError(hPort, dwError, @ComStat) then
raise Exception.Create(‘Error clearing port’);
dwRead := ComStat.cbInQue;
if dwRead > 0 then
begin
if not ReadFile(hPort, Buf, dwRead, dwRead, @OverRead) then
raise Exception.Create(‘Error reading port’);
// В Buf находятся прочитанные байты
// Далее идет обработка принятых байтов
end;
end; {while}
end;
В приведенном примере в потоке крутится цикл, тем самым инициируется
ожидание события порта вызовом функции WaitCommEvent(), ожидание же
самого этого события задается функцией WaitForSingleObject(). Для определения
количества принятых символов используется функция ClearCommError().
Когда количество принятых символов (dwRead) известно, непосредственное
чтение символов выполняется функцией ReadFile().
Используя вышеописанные выкладки, я написал на Borland Delphi 7 класс
TComPort для работы с COM-портами. Для проверки работоспособности программы
я просто соединил нуль-модемным кабелем два COM-порта на своем компьютере
и запустил два экземпляра программы для каждого порта. Данные передаются
через один порт и одновременно принимаются через другой.
Для передачи и приема данных предусмотрены отдельные окна. Формат передаваемых
данных — строка. Принимаемые данные представляются в виде массива байт.
Статьи на смежную тематику:
Allen Denver, Serial Communications in Win32 ( http://msdn.microsoft.com/library/en-us/dnfiles/html/msdn_serial.asp).
Дмитрий Кузан, Работа с портами ввода-вывода в Delphi ( http://www.delphikingdom.ru/mastering/ports1.htm).
Олег Титов, Работа с коммуникационными портами (COM и LPT) в программах для Win32 ( http://www.happytown.ru/prog/practika/com_win32.html).
Оригинал статьи http://www.mycomp.com.ua/. Автор статьи Игорь ПАВЛОВ.
ComPort Library
ComPort library with USB COM port disconnected issue fixed. Solution isn’t perfect and still is possible to suspend application on infinite WaitForSingleObject, especially in the case of very slow transmission speeds (300-600 bauds), but it’s a lot better than before.
When USB COM port disconnected error occures, handler OnAfterClose is executed and exception «Port Disconnected» (TComException = CE_PortDisconnected) is generated.
The ComPort Library contains code to access COM Ports. Originally, the COM port was the name of the serial port interface of IBM-PC compatible computers.
While nowadays COM ports get less important for communication in favor for USB access, it is still in use as virtual ports, especially as simple communication protocol for maker boards.
This library is a fork of the original ComPort Library 4.10, which can found here: https://sourceforge.net/projects/comport/
As it is a fork, some of the original commit messages are still accessible.
The master branch is focused on Delphi and all CBuilder support has been removed. However, the old CBuilder code is still accessible in the LegacyCBuilderSupport branch
Requirements
Delphi: 7, 2005, 2006, 2007, 2010, XE, XE2, XE3, XE4, XE5, XE6, XE7, XE8, RS10, RS10.1, RS10.2, RS10.3
Authors
Original Author: Dejan Crnila 1998-2002 (dejancrn@yahoo.com)
Former Maintainers: Lars Dybdahl (Lars@dybdahl.dk), Paul Doland (cport@pauld.ml1.net), Brian Gochnauer (brian@gochanuer.net)
Package names
Delphi Version | Run-Time | Design-Time | State |
---|---|---|---|
Delphi 7 | .\Packages\D7\CPortLib7.dpk | .\Packages\D7\DsgnCPort7.dpk | not sufficiently tested |
Delphi 8 | .\Packages\D8\CPortLib8.dpk | .\Packages\D8\DsgnCPort8.dpk | not sufficiently tested |
Delphi 2005 | .\Packages\D2005\CPortLib2005.dpk | .\Packages\D2005\DsgnCPort2005.dpk | not sufficiently tested |
Delphi 2006 | .\Packages\D2006\CPortLib2006.dpk | .\Packages\D2006\DsgnCPort2006.dpk | not sufficiently tested |
Delphi 2007 | .\Packages\D2007\CPort_R.dpk | .\Packages\D2007\CPort_D.dpk | not sufficiently tested |
Delphi 2009 | .\Packages\D2009\CPort_R.dpk | .\Packages\D2009\CPort_D.dpk | not sufficiently tested |
Delphi 2010 | .\Packages\D2010\CPort_R.dpk | .\Packages\D2010\CPort_D.dpk | should work |
Delphi XE | .\Packages\XE\CPort_R.dpk | .\Packages\XE\CPort_D.dpk | should work |
Delphi XE2 | .\Packages\XE2\CPort_R.dpk | .\Packages\XE2\CPort_D.dpk | tested |
Delphi XE3 | .\Packages\XE3\CPort_R.dpk | .\Packages\XE3\CPort_D.dpk | should work |
Delphi XE4 | .\Packages\XE4\CPort_R.dpk | .\Packages\XE4.dpk | should work |
Delphi XE5 | .\Packages\XE5\CPort_R.dpk | .\Packages\XE5\CPort_D.dpk | should work |
Delphi XE6 | .\Packages\XE6\CPort_R.dpk | .\Packages\XE6\CPort_D.dpk | should work |
Delphi XE7 | .\Packages\XE7\CPort_R.dpk | .\Packages\XE7\CPort_D.dpk | tested |
Delphi XE8 | .\Packages\XE8\CPort_R.dpk | .\Packages\XE8\CPort_D.dpk | should work |
Delphi RS 10 | .\Packages\10S\CPort_R.dpk | .\Packages\10S\CPort_D.dpk | should work |
Delphi RS 10.1 | .\Packages\101B\CPort_R.dpk | .\Packages\101B\CPort_D.dpk | should work |
Delphi RS 10.2 | .\Packages\102T\CPort_R.dpk | .\Packages\102T\CPort_D.dpk | tested |
Delphi RS 10.3 | .\Packages\103R\CPort_R.dpk | .\Packages\103R\CPort_D.dpk | should work |
Installation
Remove all previously installed files of ComPort Library (TComPort component). Create a new folder under Delphi directory and extract sources zip file into new folder. Add to the Library Path the new ComPort folder (Tools-Environment Options-Library-Library Path).
For Delphi 2006:
Use the instructions below for all other Delphi versions. I recommend installing CPort while running Delphi 2006 in the «all personalities» mode of Delphi, rather than in the Delphi only personality. That helps make sure CPort gets installed dual-mode.
Use «File/Open» menu item in Delphi IDE to open ComPort run-time package source file (see above). Click «Compile» button in Package window to compile the library. Now move run-time package library file or files (see above) from ComPort folder to a folder that is accessible through the search PATH (e.g. Windows\System32).
Now you have to install design-time package. Use File/Open menu item to open design-time package source file (see above). Click «Compile» button in Package window to compile the package and «Install» button to register ComPort into the IDE. ComPort components appear in «CPortLib» page of component pallete. If it complains that it can’t install it because it can’t find a library, you probably did not put the run-time package in the search path. You might not get this error until the next time you try to start Delphi.
Note: Do not save packages under Delphi IDE.
Examples
ComExample.dpr — shows some basic send-recieve features
ModTest.dpr — modem test console application
MiniTerm.dpr — simple terminal application
CPortMonitor.pas — TCPortMonitor component for monitoring incoming and outgoing data. This example shows how to link to TCustomComPort component. Author: Roelof Y. Ensing (e-mail: ensingroel@msn.com).
BarCodeScanner.pas — TBarCodeScanner component. An example of simple TCustomComPort descendant.
Installing help file (not available for Delphi2005)
In Delphi, go to the Help menu, customize item. You should be presented with a tabbed notebook, «Contents» tab selected. Click the + (Add Files) button. Browse to the CPort directory. Select CPort.toc.
Click on the «Index» tab. Click Add Files. Select CPort.hlp. Click on the «Link» tab. Click Add Files. Select CPort.hlp. Select File Menu/Save Project Item. Exit program. Note that Borland’s OpenHelp utility does not prompt you if you close the program and forget to save your changes, so you must remember to do so yourself.
Known problems and issues
1.) OnRxBuf event handler problem in Delphi IDE
If user double clicks on OnRxBuf event in Delphi IDE, message pops up
saying: "Property and Method ComPort1RxBuf are not compatible".
This is a Delphi IDE bug, since it can't handle untyped parameters
like Buffer parameter of OnRxBuf event.
Solution: Application has to assign OnRxBuf handler manually in code.
2.) No integrated help for Delphi 2005 or 2006.
Solution: Manually open .HLP file with Windows Help.
comport411f — Компонент для работы с Com-портом.
Скачать его, можно ЗДЕСЬ.
Перед вами самое полное руководство по установке этого компонента! Читайте и выполняйте инструкции пошагово.
Поехали!
- Скопируем (Распакуем) куда либо наш компонент comport411f.
- Откроем среду разработки Delphi.
Сначала необходимо добавить папку Source компонента в Library Path среды разработки.
- Переходим в опции.
- Выбираем элемент Library в дереве опций.
- Выбираем версию платформы.
- Нажимаем кнопку обзора в Library Path.
- Нажимаем кнопку обзора файловой системы в списке путей к библиотекам.
- Переходим к папке Source нашего компонента comport411f.
- Выбираем её.
- Добавляем в список (Add).
- Нажимаем (OK).
- Сохраняем (Save).
Теперь откроем файлы проекта нашего компонента.
- Перейдем в папку компонента.
- Откроем CPortLibD2010.dproj.
Примечание: На этом этапе вы можете, с помощью зажатой кнопки Ctrl, выбрать сразу два файла: CPortLibD2010.dproj и DsgnCPortD2010.dproj. Тогда следующую часть этой инструкции, касаемо добавления файла DsgnCPortD2010.dproj можете не читать.
Если вы новичок или устанавливайте компонент в первый раз, не обращайте внимания на это примечание и следуйте шагам этой справки!
Здесь среда разработки немного поругается. Нажмите Ignore All.
Итак, файл — CPortLibD2010.dproj добавили, теперь добавим к группе проекта, второй файл — DsgnCPortD2010.dproj нашего компонента comport411f.
- Перейдём в панель менеджера проекта (project manager). Если этой панели вы не видите, нажмите Ctrl+Alt+F11, панель должна отобразиться.
- Правой кнопкой мыши нажмите на группу проекта (ProjectGroup1).
- Выберите — Добавить существующий проект (Add Existing Project).
- Откроем DsgnCPortD2010.dproj.
После того, как были добавлены оба файла, проводим с ними следующие манипуляции:
Нажимаем правой кнопкой мыши, сначала на CPortLibD2010.bpl.
- Перестраиваем и компилируем его.
Затем, правой кнопкой мыши, на DsgnCPortD2010.bpl.
- Перестраиваем и компилируем его.
Теперь установим компонент.
- Правой кнопкой мыши нажмём на DsgnCPortD2010.bpl.
- Установим (Install).
- Должно будет появиться такое окно информации.
- Нажмём OK.
Всё, компонент — comport411f установлен!
- Теперь закроем среду разработки.
- Будут выходить разные предупреждения. Везде нажимаем НЕТ или НЕТ ДЛЯ ВСЕХ.
Теперь всё!
Спасибо за внимание!