Учебная работа. Реферат: Ещё раз о прямом доступе к аппаратуре

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (7 оценок, среднее: 4,86 из 5)
Загрузка...
Контрольные рефераты

Учебная работа. Реферат: Ещё раз о прямом доступе к аппаратуре

Ещё раз о прямом доступе к аппаратуре

Сивцов Павел

Преамбула

В один прекрасный момент мой знакомый попросил написать ему ординарную программку — «сторожевой пёс». Всё, что необходимо созодать — это отловить момент размыкания либо замыкания наружного контакта и при пришествии такового действия запустить другую программку. работать программка обязана под Windows XP. Задачка смотрелась простой. Единственное, что не хотелось созодать — аппаратную часть. Т.е. идеальнее всего было бы отыскать такое решение, при котором практически ничего не надо было бы паять.

Довольно стремительно выяснилось, что проще всего для таковой цели употреблять опрос состояний LPT- либо COM-портов. здесь и начинается самое увлекательное.

LPT

Для реализации «сторожевого пса» на LPT-порту можно употреблять повторяющийся опрос состояния неких его контактов. Можно просто выявлять состояния линий SELECTED (контакт 13), BUSY (контакт 11) и PAPER EMPTY (контакт 12). Довольно замыкать/размыкать избранный контакт с «землей» (контакты 18–25). Я избрал внедрение BUSY — замыкал контакты 11 и 23. Итак, аппаратная часть выходила простой, сейчас необходимо было как-то достучаться до избранного контакта с программной стороны. Здесь-то и повстречалась 1-ая сложность — законных методов прямого доступа к портам в линейке Windows NT нет. Употреблять примочки типа gwio.sys, разрешающие прямой доступ к аппаратуре, весьма не хотелось. Работа с портом как с файлом в данном случае не подступает, т.к. необходимо не данные читать, а опрашивать состояния. Тем не наименее, опосля долгого исследования MSDN, законный доступ к неким линиям порта был найден! метод этот — доступ к порту через функцию DeviceIoControl(…, IOCTL_PAR_QUERY_INFORMATION, …). здесь нашлась 2-ая сложность — отсутствие подходящих заголовочных файлов для Delphi. Пришлось без помощи других портировать ntddpar.h из DDK. Портированный файл получил заглавие JwaNtDdPar.pas и был разлюбезно добавлен Marcel van Brakel в JEDI Windows API Library.

Маленькой пример показывает итоговый код. Delphi 7.

Пример кода

uses

SysUtils, JwaWinType, JwaWinNT, JwaWinBase, JwaNtDdPar;

{$WARN SYMBOL_PLATFORM OFF}

function GetLptStatus: Boolean;

var

eFileHandle: THandle;

eInfo: TParQueryInformation;

eBytesReturned: DWORD;

begin

// откроемпорт

eFileHandle := CreateFile(‘LPT1’, GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);

Win32Check(eFileHandle <> INVALID_HANDLE_VALUE);

try

// узнаемсостояние

Win32Check(DeviceIoControl(eFileHandle, IOCTL_PAR_QUERY_INFORMATION, nil, 0,

@eInfo, SizeOf(eInfo), @eBytesReturned, nil));

Result := (Byte(eInfo.Status) and PARALLEL_BUSY) = 0;

finally

// не забудьте закрыть хендл по окончанию работы

Win32Check(CloseHandle(eFileHandle));

end;

end;




Маленький и стильный код, не правда ли? Для решения поставленной задачки довольно опрашивать состояние порта раз-другой за секунду. В принципе, естественно же, лучше сходу открыть порт при старте, а закрыть по окончанию.

ПРЕДУПРЕЖДЕНИЕ

К огорчению, не получится открыть порт в режиме FILE_FLAG_OVERLAPPED, чтоб потом употреблять достоинства асинхронной работы. Поточнее, порт открыть получится, не получится получить событие при изменении статуса линий порта.

Зато этот код удачно отработал из-под гостевой учетной записи под Windows XP.




Крайний аспект — дребезг контактов. «Дребезг контактов — это явление неоднократного неконтролируемого замыкания и размыкания контактов в моменты их соприкосновения и расхождения». Продолжаются такие переходные процессы в клавишах около 10-15 миллисекунд. Т.е. с большенный вероятностью мы будем получать неверные срабатывания нашего кода, если интервал меж проверками будет короче.

Надеюсь, этот пример работы с LPT-портом послужит неплохой демонстрацией того, как в почти всех вариантах просто получить законный доступ к аппаратуре без написания драйверов либо обхода Hardware Abstraction Layer. Не для того этот HAL придумывали, чтоб его обходить.

Доводилось читать о вариантах захвата порта спулером печати, но на практике такую ситуацию повстречать не удалось. Если кто-либо сумеет прояснить этот вопросец, я буду рад.

ПРИМЕЧАНИЕ

К слову, посреди странички HTTP://cooler.irk.ru/cl190902.html изложено довольно увлекательное письмо, в каком описывается работа с портом в режиме IEEE_COMPATIBILITY. Таковой режим дозволяет с минимумом телодвижений обеспечить настоящий вывод данных на самодельное LPT- устройство.




COM

При использовании COM-порта задачка обнаружения наружного действия быть может решена ещё проще. Довольно замыкать/размыкать контакты 7 (RTS) и 8 (CTS) у девятиконтактного разъема (снова ничего не придется паять) и инспектировать наличие сигнала CTS. При этом опрос можно создавать через обычный CommApi.

Пример кода

function GetComStatus: Boolean;

var

eFileHandle: THandle;

eStatus: DWORD;

begin

// откроемпорт

eFileHandle := CreateFile(‘COM1’, GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);

Win32Check(eFileHandle <> INVALID_HANDLE_VALUE);

try

// узнаемсостояние

Win32Check(GetCommModemStatus(eFileHandle, eStatus));

Result := (eStatus and MS_CTS_ON) > 0;

finally

// не забудьте закрыть хендл по окончанию работы

Win32Check(CloseHandle(eFileHandle));

end;

end;




Вообщем, этот пример элегантным уже не назовешь, т.к. внедрение COM-порта дает возможность избавиться от повторяющегося опроса, используя асинхронную работу через WaitCommEvent и WaitForMultipleObjects.

Ниже приведен код примера. Для пояснения сущности происходящего код обильно прокомментирован. Но все-же обращу внимание на некие аспекты:

WaitForMultipleObjects ожидает нескончаемо. Никаких повторяющихся опросов — означает и никакого употребления ресурсов. Всё реализовано на событиях.

Нет необходимости в TerminateThread для принудительного прекращения выполнения потока. Выполнение быть может «культурно» завершено в хоть какой момент. Для этого употребляется отдельное событие.

Обычная реализация неблокирующей задержки для угнетения дребезга. Так как повторяющийся опрос мы не применяем, то, чтоб избавиться от неверных срабатываний программным методом, необходимо подождать несколько 10-ов миллисекунд, и если состояние за этот период времени не поменялось, то замыкание/размыкание цепи состоялось. В качестве таймера употребляется WaitableTimer. Обратите внимание на его теоретическую точность.

Главный способ

procedure TComWatchdogThread.Execute;

var

// структура, применяемая Win32 для хранения внутренней инфы при

// асинхронной работе. ничего не считая поля hEvent нам от неё не требуется

eOverlapped: TOverlapped;

// запрос ожидания асинхронного действия конфигурации состояния порта

//…………………………………………………………………

procedure InitWaitCommEvent;

var

eEventMask: DWORD;

begin

// ошибки ERROR_IO_PENDING необходимо просто игнорировать — их наличие значит

// лишь то, что крайняя операция с портом ещё не завершена.

// Что любопытно, недозволено два раза попорядку вызвать WaitCommEvent, т.е.

// запросил событие — означает, дождись его.

if not WaitCommEvent(FComHandle, eEventMask, @eOverlapped)

and (GetLastError <> ERROR_IO_PENDING) then

RaiseLastOSError;

end;

var

// TWOHandleArray — этопростоготовыймассивиз 64 хендловдля

// функции WaitForMultipleObjects. Мы используем лишь 3 хендла,

// но для простоты воспользуемся готовым массивом на 64, чтоб

// не связываться с ручным распределением памяти.

eHandles: TWOHandleArray;

eTime: Int64;

eStatus: DWORD;

eStubInstalled: Boolean;

begin

// заполним структуры для асинхронной работы

FillChar(eOverlapped, SizeOf(eOverlapped), 0);

eOverlapped.hEvent := FChangeEvent;

eHandles[0] := eOverlapped.hEvent;

eHandles[1] := FTerminateEvent;

eHandles[2] := FFlutterTimer;

// 0.5 секундыдля SetWaitableTimer

eTime := -5000000;

// ничего для себя точность, не правда ли?

// за ранее взведем флаг ожидания действия, чтоб цикл заработал

InitWaitCommEvent;

while not Terminated do

case WaitForMultipleObjects(3, @eHandles, False, INFINITE) of

// при изменении состояния порта

WAIT_OBJECT_0:

begin

// снова взведем флаг ожидания действия

InitWaitCommEvent;

// для угнетения дребезга создадим маленькую задержку. Если состояние

// порта поменяется резвее, чем истечет время задержки (дребезг), то

// таймер просто будет переведен «на попозже».

Win32Check(SetWaitableTimer(FFlutterTimer, eTime, 0, nil, nil,

False));

end;

// при запросе принудительного окончании потока

WAIT_OBJECT_0 + 1:

// немедленный выход — окончание выполнения потока

Exit;

// опосля задержки для угнетения дребезга

WAIT_OBJECT_0 + 2:

begin

// узнаемсостояние

Win32Check(GetCommModemStatus(FComHandle, eStatus));

eStubInstalled := (eStatus and MS_CTS_ON) > 0;

// вызовемобработчиксобытия

if eStubInstalled then

DoOnChange;

end;

// при неожиданной ошибке

WAIT_FAILED:

// случилосьстрашное…

RaiseLastOSError;

end;

end;




Полный код примера приведен в прилагаемом к статье архиве.

Перечень
литературы

JEDI Windows API Library HTTP://members.chello.nl/m.vanbrakel2/win32api.zip

Parallel Port Central HTTP://www.lvr.com/parport.htm

Serial Port Central http://www.lvr.com/serport.htm

Письмо в журнальчик «Cooler» о прямом доступе к LPT HTTP://cooler.irk.ru/cl190902.html

]]>