Sources
Delphi Russian Knowledge Base
DRKB - база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Что такое порт? Правила для работы с портами

01.01.2007
Дмитрий Кузан, kuzan@fsskomi.parma.ru

Известно, что в компьютере очень много собрано различных устройств. Возникает вопрос, как операционная система общается с ними. Для этого и служит порт, то есть это «дверь», через которую программа (операционная система) может управлять данным устройством (считывать данные, заносить их).

Причем я разделяю порты на две категории (это чисто мое разделение) - порты общеизвестные (COM LPT) и порты внутренние, служащие для связи с внутренними устройствами ЭВМ.

Некоторые правила для работы с портами

Следует иметь в виду, что при разработке программ, имеющих дело с работой с портами, следует учитывать следующие факторы:

а) Стараться использовать функции высокого уровня для доступа к портам (в частности WinAPI) и не прибегать к низкоуровневым операциям чтения/записи портов. Если вы все-таки решили писать низкоуровневое чтение, то эти процедуры нужно выносить в отдельную DLL или VXD, по следующим причинам - известно, что операционная система Windows95/98 а особенно NT являются по своей сути многозадачными системами. То есть если ваша программа обращается конкретно к порту не через динамический вызов функции DLL или VXD (использования механизма DLL) а напрямую то это может сказаться на корректной работе системы или даже завалить ее. И даже если в Windows95/98 такой подход вполне может работать то в Windows NT вследствие его архитектуры не разрешит непосредственное чтение/запись напрямую, а использование механизма DLL или VXD позволяет обойти эту проблему.

б) Если вы работаете с каким-то нестандартным портом ввода-вывода (например портом хранящим состояние кнопок пульта ДУ TVTunera, то наверняка в комплекте поставки родного софта найдется DLL или VXD для управления этим устройством и отпадет нужда писать код, так я при работе с пультом ДУ TVTunerа использую стандартную DLL, поставляемую в комплекте, это сразу решило вопросы связанные с управлением портами данного тюнера)Итак, отступление - немного практики...

Маленький пример для работы с портами

(первый пример был уже опубликован в королевстве Дельфи и представлял собой пример работы с весами ПетрВес)

function PortInit : boolean; //инициализация
var f: THandle; 
  ct: TCommTimeouts;
  dcb: TDCB;
begin
  f := Windows.CreateFile(PChar('COM1'), GENERIC_READ or 
    GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE,
    nil, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, 0);
  if (f < 0) or not Windows.SetupComm(f, 2048, 2048)or not
    Windows.GetCommState(f, dcb) then exit; //init error dcb.BaudRate := скоpость;
  dcb.StopBits := стоп-биты;
  dcb.Parity := ?етность;
  dcb.ByteSize := 8;
  if not Windows.SetCommState(f, dcb) or 
     not Windows.GetCommTimeouts(f, ct) then exit; //error
  ct.ReadTotalTimeoutConstant := 50;
  ct.ReadIntervalTimeout := 50;
  ct.ReadTotalTimeoutMultiplier := 1;
  ct.WriteTotalTimeoutMultiplier := 0;
  ct.WriteTotalTimeoutConstant := 10;
  if not Windows.SetCommTimeouts(f, ct)
    or not Windows.SetCommMask(f, EV_RING + EV_RXCHAR + EV_RXFLAG + EV_TXEMPTY)
    then exit; //error
  result := true;
end;

function DoneComm: boolean; //закpыть поpт
begin
  result := Windows.CloseHandle(f);
end; 

function PostComm(var Buf; size: word): integer; //пеpеда?а в поpт
  var p: pointer; i: integer;
begin
  p := @Buf;
  result := 0;
  while size > 0 do 
  begin
    if not WriteFile(f, p^, 1, i, nil) then exit;
    inc(result, i); inc(integer(p)); dec(size);
    Application.ProcessMessages;
  end;
end; 

function ReadComm(var Buf; size: word): integer; //пpием из поpта
  var i: integer; ovr: TOverlapped;
begin
  fillChar(buf, size, 0);
  fillChar(ovr, sizeOf(ovr), 0); i := 0; result := -1;
  if not windows.ReadFile(f, buf, size, i, @ovr) then exit;
  result := i;
end; 

Данный пример был взят мной из многочисленный FAQ посвященных в DELPHI в сети ФИДО

Итак, для работы с портами COM и LPT нам понадобится знание функций Windows API. Вот подробное описание функций, которые нам нужны (в эквиваленте C) для работы с портами.

(извините за возможный местами неточный перевод, если что - поправьте меня, если что не так перевел)

CreateFile HANDLE CreateFile(
  LPCTSTR lpFileName,// указатель на строку PCHAR с именем файла
  DWORD dwDesiredAccess,// режим доступа
  DWORD dwShareMode,// share mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,// указатель на атрибуты
  DWORD dwCreationDistribution,// how to create
  DWORD dwFlagsAndAttributes,// атрибуты файла
  HANDLE hTemplateFile // хендл на temp файл
);

Пример кода на Дельфи

< вырезано >
CommPort := 'COM2'; 
hCommFile := CreateFile(Pchar(CommPort), GENERIC_WRITE, 0, nil, 
  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
< вырезано >

Параметры

Возвращаемые значения:

Если функция преуспевает, возвращаемое значение - открытый дескриптор к указанному объекту(файлу). Если файл не существует - 0.

Если произошли функциональные сбои, возвращаемое значение - INVALID_HANDLE_VALUE.

Чтобы получить расширенные данные об ошибках, вызовите GetLastError. Обратите внимание !

Для портов, dwCreationDistribution параметр должен быть OPEN_EXISTING, и hTemplate должен быть Nil. Доступ для чтения-записи должен быть определен явно. SECURITY_ATTRIBUTES Структура содержит описание защиты для объекта и определяет, может ли дескриптор быть унаследован дочерними процессами.

typedef struct _SECURITY_ATTRIBUTES {
  DWORD nLength;
  LPVOID lpSecurityDescriptor;
  BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;

Параметры:

Замечания

Указатель на структуру SECURITY_ATTRIBUTES используется как параметр в большинстве функций работы с окнами в Win32 API.

Структура DCB

Структура DCB определяет установку управления для последовательного порта ввода-вывода (нам она понадобится для разбора примера с программой управления весами ПетрВес)

Примечание: В местах где нельзя дать точный перевод будет дано определение на английском из MSDK и приблизительный его перевод.

Описание в эквиваленте C

typedef struct \_DCB { // dcb
  DWORD DCBlength; // Размер DCB
  DWORD BaudRate; // Скорость пересылки данных в бодах;
  // текущая скорость в бодах
  DWORD fBinary: 1; // binary mode, no EOF check
  // двоичный режим, не проверять конец данных (по умолчанию значение = 1)
  DWORD fParity: 1; // Включить проверку четность (по умолчанию значение = 1)
  DWORD fOutxCtsFlow:1; // CTS управление потоком выхода
  DWORD fOutxDsrFlow:1; // DSR управление потоком выхода
  DWORD fDtrControl:2; // DTR Тип управления потоком скорости передачи данных
  DWORD fDsrSensitivity:1; // DSR sensitivity (чувствительность)
  DWORD fTXContinueOnXoff:1; // XOFF continues Tx (стоп-сигнал продолжает выполнение)
  DWORD fOutX: 1; // XON/XOFF out flow control
  // (СТАРТ-СИГНАЛ / СТОП-СИГНАЛ для управления выходящим потоком
  // (по умолчанию значение = 1)
  DWORD fInX: 1; // XON/XOFF in flow control
  // (СТАРТ-СИГНАЛ / СТОП-СИГНАЛ для управления входящим потоком
  // (по умолчанию значение = 1)
  DWORD fErrorChar: 1; // enable error replacement
  // (включить проверку погрешностей по умолчанию=1)
  DWORD fNull: 1; // enable null stripping
  // (отвергать пустой поток данных (по умолчанию=1))
  DWORD fRtsControl:2; // RTS управление потоком данных
  DWORD fAbortOnError:1; // abort reads/writes on error
  // (проверять операции чтения/записи по умолчанию=1)
  DWORD fDummy2:17; // reserved ЗАРЕЗЕРВИРОВАНО
  WORD wReserved; // not currently used (НЕ ДЛЯ ИСПОЛЬЗОВАНИЯ)
  WORD XonLim; // transmit XON threshold (порог чувствительности старт-сигнала)
  WORD XoffLim; // transmit XOFF threshold (порог чувствительности стоп-сигнала)
  BYTE ByteSize; // Бит в байте (обычно 8)
  BYTE Parity; // 0-4=no,odd,even,mark,space (четность байта)
  BYTE StopBits; // 0,1,2 = 1, 1.5, 2 (стоповые биты)
  char XonChar; // Tx and Rx XON character (вид старт сигнал в потоке)
  char XoffChar; // Tx and Rx XOFF character (вид стоп сигнал в потоке)
  char ErrorChar; // error replacement character (какой сигнал погрешности,его вид)
  char EofChar; // end of input character (сигнал окончания потока)
  char EvtChar; // received event character РЕЗЕРВ
  WORD wReserved1; // reserved; do not use (НЕ ДЛЯ ИСПОЛЬЗОВАНИЯ)
} DCB;

Вызов на Дельфи:

with Mode do
Begin
  BaudRate := 9600;
  ByteSize := 8;
  Parity := NOPARITY;
  StopBits := ONESTOPBIT; // одиночный стоп-бит
  Flags := EV_RXCHAR + EV_EVENT2;
End;

Параметры:

Примечание: Под Windows 3.1 небинарный способ передачи допускается, но для работы данного способа необходимо заполнит параметр EofChar который будет восприниматься конец данных.

Дополнение: Когда структура DCB использует «ручной» выбор конфигурации, следующие ограничения используются для ByteSize и StopBits параметров:

Previous page:
Как прочитать байт из параллельного порта?
Top:
DRKB
Next page:
Как читать / писать в I/O порты?