Сообщений 0    Оценка 45        Оценить  
Система Orphus

Программные интерфейсы джойстика и таймера

Автор: Евгений Музыченко
Источник: Компьютер Пресс
Опубликовано: 28.07.2003
Исправлено: 10.12.2016
Версия текста: 1.0

Краткие сведения об аппаратном устройстве джойстиков
Основные черты и понятия интерфейса джойстика
Типы джойстиков
Номера устройств
Логические значения координат
Калибровка
Частота опроса
Режим захвата
Уведомление об изменении состояния
Дребезг
Порог чувствительности при уведомлении
Основные черты и понятия интерфейса таймера
Разрешение
Виды событий
Уведомление о наступлении событий
Точность
Общая схема взаимодействия программы и подсистем
Средства разработки, включаемые файлы и библиотеки
Структуры, используемые в интерфейсе
Структура JOYCAPS
Структура JOYINFO
Структура JOYINFOEX
Структура TIMECAPS
Структура MMTIME
Уведомления, передаваемые программе
Уведомление о состоянии джойстика
Уведомление о наступлении таймерного события
Набор интерфейсных функций подсистем
Перечень интерфейсных функций
Значения, возвращаемые интерфейсными функциями
Описание интерфейсных функций джойстика
joyGetNumDevs - запрос количества устройств
joyGetDevCaps - запрос параметров и возможностей джойстика
joyGetPos - запрос текущего состояния традиционного джойстика
joyGetPosEx - запрос текущего состояния любого джойстика
joyGetThreshold - запрос порога чувствительности к перемещению
joySetThreshold - установка порога чувствительности к перемещению
joySetCapture - захват джойстика
joyReleaseCapture - освобождение джойстика
Описание интерфейсных функций таймера
timeGetDevCaps - запрос параметров и возможностей таймера
timeGetSystemTime - запрос системного времени в виде структуры
timeGetTime - запрос системного времени в миллисекундах
timeBeginPeriod - начало работы с заданным разрешением
timeEndPeriod - конец работы с заданным разрешением
timeSetEvent - запрос таймерного события
timeKillEvent - отмена таймерного события
TimerProc - функция приложения, вызываемая при уведомлении
Недостатки подсистем джойстика и таймера
Нетрадиционные применения интерфейса джойстика

Последние две подсистемы Windows MME, оставшиеся неописанными в рамках данного цикла - это интерфейсы с джойстиками (Joystick) и таймерами (Timer). Назначение первого понятно, а второй служит для управления мультимедийными виртуальными таймерами (multimedia timers).

Обе подсистемы введены в Windows 3.1, и впоследствии стали стандартными компонентами Win32.

Описание подсистем есть во встроенной системе помощи любой современной среды программирования, а также в MSDN SDK. Microsoft поддерживает в Internet справочную систему MSDN Online, где интерфейс джойстика описан в разделе http://msdn.microsoft.com/library/psdk/multimed/joy_42er.htm, а интерфейс таймера - в разделе http://msdn.microsoft.com/library/psdk/multimed/mmtime_4msz.htm.

Поскольку оба интерфейса предельно просты, я счел излишним снабжать статью демонстрационной программой, в которой не удалось бы показать возможности интерфейсов без дополнительной идеи и объемного кода, не имеющего прямого отношения к предмету.

Краткие сведения об аппаратном устройстве джойстиков

Традиционный джойстик IBM PC представляет собой простое аналоговое (analog, analogue) устройство, состоящее из двух переменных резисторов сопротивлением 100 кОм и двух кнопок с контактами на замыкание. Оси резисторов через систему ортогонального привода связаны с рукояткой джойстика таким образом, что горизонтальное перемещение рукоятки приводит к повороту оси резистора X, а вертикальное - резистора Y. То есть, перемещение рукоятки в двух измерениях раскладывается на два независимых перемещения в перпендикулярных направлениях - X и Y. Направления X и Y называются осями (axis) джойстика.

Кнопки джойстика нумеруются от 1 до максимума; кнопка 1 обычно размещается на конце рукоятки.

Адаптер джойстика раньше входил в состав большинства многофункциональных адаптеров ввода/вывода (Multi I/O, или мультикарт), а впоследствии стал обязательной частью типовой звуковой карты универсального назначения. Интерфейс также предельно прост: ток, протекающий через каждый из переменных резисторов, заряжает времязадающий конденсатор одновибратора, который срабатывает через интервал времени, пропорциональный сопротивлению резистора. Срабатывание одновибраторов отражается в соответствующих разрядах порта состояния джойстика. Другие разряды порта состояния сигнализируют о замыкании контактов кнопок.

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

Параметры времязадающих цепей интерфейса подобраны таким образом, чтобы время заряда конденсатора до порога срабатывания не превышало нескольких микросекунд (от десятков до сотен проходов цикла ожидания). Если одновибратор не успел сработать в течение нескольких тысяч проходов цикла - это означает, что джойстик не подключен вообще.

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

Кроме традиционных аналоговых джойстиков, существуют цифровые (digital), что попросту означает наличие вместо переменных резисторов переключателей, срабатывающих при наклонах рукоятки. Такие джойстики сигнализируют только о наклоне рукоятки в одном из восьми направлений, но не о величине или скорости перемещения. По сути, такой "цифровой" джойстик содержит постоянные резисторы, подключаемые к интерфейсу при замыкании контактов переключателей.

Стандартный адаптер является двухканальным, то есть - допускает подключение двухнезависимых джойстиков, что часто используется играми с двумя игроками. Поскольку все органы управления отслеживаются независимо, возможно также объединение двух джойстиков в один с количеством осей и кнопок до четырех. Таким образом реализуются джойстики с осями R (обычно ось X второго канала) и Z (обычно ось Y второго канала).

Также существуют джойстики со специальными (proprietary) интерфейсами, выполненными в виде отдельных адаптеров. Такие интерфейсы позволяют подключать джойстики с большим количеством осей перемещения, кнопок, переключателей и других элементов управления.

Основные черты и понятия интерфейса джойстика

Типы джойстиков

В Win16 поддерживаются традиционные джойстики, имеющие до трех осей (X, Y, Z) и до четырех кнопок. Джойстики, имеющие более двух осей и/или двух кнопок, используют объединение каналов одного стандартного адаптера.

Win32 поддерживает различные типы джойстиков, имеющих до шести осей перемещения манипуляторов, и до 32 кнопок (переключателей), имеющих два состояния (нажато/отпущено, включено/выключено и т.п.).

Кроме традиционных осей X, Y и Z, подсистема поддерживает ось R (Rudder - руль), и две дополнительных оси U и V. Перемещения по каждой из осей отслеживаются независимо от других, и конструктивно джойстик может быть выполнен как с совмещенными, так и с раздельными манипуляторами.

Ряд джойстиков имеет манипулятор точки обзора POV (point of view), которым обычно переключаются виды в играх (вперед, назад, в стороны). Значения этого манипулятора представляются в градусах по часовой стрелке относительно направления вперед.

В традиционном джойстике значения координат по осям U и V вычисляются, как среднее арифметическое между двумя координатами одного канала адаптера. Координата U вычисляется по первому каналу, V - по второму. Значение POV вычисляется из координаты Z, если она есть, иначе - из координаты R. Другими словами - при нехватке физических осей значения координат будут взаимозависимыми, и приложения должны использовать только одну из зависимых координат.

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

Номера устройств

В отличие от прочих подсистем Windows MME, устройство джойстика не требует обязательного открывания перед сеансом работы. Вместо этого в каждой функции интерфейса указывается номер устройства в системе. Как и все в MME, устройства джойстиков нумеруются с нуля.

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

Логические значения координат

Количество проходов цикла ожидания до срабатывания одновибратора называется "сырой" (raw) позицией джойстика, и используется в основном внутри подсистемы. Для удобства работы драйвер джойстика приводит эти значения к нормированным, логическим (logical) величинам, представляемым числами в диапазоне 0..65535 (значения типа WORD). Если не определено иначе, программа получает по запросам значения именно логических координат, а "сырые" остаются только на уровне драйвера и служебных программ.

Калибровка

Для получения логических значений координат аналоговый джойстик после установки нуждается в калибровке. Калибровка заключается в установке манипуляторов в заданные положения, для которых драйвер по "сырым" координатам вычисляет поправочные коэффициенты для приведения их к логическим.

Калибровка (а также первоначальная установка джойстика) выполняется с помощью мастера (wizard), запускаемого из панели управления иконкой "Игровые контроллеры".

Частота опроса

Традиционный интерфейс джойстика не поддерживает генерации прерываний при изменении состояния манипуляторов, поэтому требуется постоянный опрос (polling) интерфейсного порта по прерываниям от системного таймера. В Windows эту задачу выполняет драйвер джойстика; приложению необходимо лишь задать требуемую частоту опроса (polling frequency). Увеличение частоты опроса приводит к повышению точности отслеживания, но одновременно - к увеличению доли накладных расходов системы, уменьшение частоты - к отставанию программных событий от реальных и снижению плавности перемещения.

Сама процедура опроса занимает сравнительно небольшое время - не более нескольких микросекунд. Накладные расходы вызываются в основном обработкой прерываний при повышенной частоте системного таймера.

Режим захвата

Информацию о состоянии каждого из джойстиков приложение может получать как путем регулярного опроса, так и посредством уведомляющих сообщений. Опрашивать джойстик можно в любой момент, но уведомляющие сообщения доступны только в режиме захвата (capture), который в некотором роде аналогичен открыванию устройства в остальных подсистемах MME. В режиме захвата Windows направляет приложению поток сообщений о состоянии джойстика.

Захватить устройство джойстика можно только один раз; последующие попытки захватить джойстик до его освобождения, даже от имени той же самой задачи, будут отвергаться с кодом ошибки JOYERR_NOCANDO.

Уведомление об изменении состояния

В режиме захвата Windows посылает заданному окну приложения сообщения, уведомляющие об изменении состояния джойстика - перемещении манипуляторов или нажатии/отпускании кнопок. Сообщения посылаются либо регулярно через заданные промежутки времени, либо нерегулярно - при каждом изменении состояния джойстика.

Дребезг

Простота используемого адаптером метода определения положения манипуляторов чревата значительными (порядка единиц процентов) случайными и систематическими ошибками, когда при неподвижном манипуляторе значение его координаты от раза к разу определяется различным. Это явление называется дребезгом (bouncing) и требует специальных мер подавления, например - установки порога чувствительности.

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

Порог чувствительности при уведомлении

По умолчанию, подсистема уведомляет приложение в режиме захвата при минимально воспринимаемом изменении положения манипуляторов. Если приложению требуется более грубое отслеживание состояния манипуляторов, можно установить порог (threshold) чувствительности, задаваемый в логических единицах перемещения (позиции). Установка порога также позволяет подавить "дребезг" значений координат, порожденный неточностью метода измерения сопротивления.

Основные черты и понятия интерфейса таймера

Разрешение

Разрешением (resolution) называется точность отсчета временных интервалов. Стандартный таймер Windows, управляемый функцией SetTimer, имеет достаточно низкое разрешение (порядка 50 мс в Windows 9x, порядка 10 мс - в Windows NT). Этого вполне хватает для отсчета интервалов при общении с пользователем, однако недостаточно для приложений реального времени. Мультимедийный таймер предоставляет возможность установки произвольного разрешения, вплоть до одной миллисекунды.

Фактически разрешение таймера означает период времени между аппаратными прерываниями от системного таймера. Если при стандартном разрешении они возникают сравнительно редко, несколько десятков раз в секунду, то при разрешении в 1 мс ядро Windows будет получать 1000 прерываний в секунду, что приводит к некоторому возрастанию накладных расходов.

Виды событий

Мультимедийный таймер поддерживает два вида событий: однократное (One shot) и периодическое (Periodic). Однократное событие возникает при истечении заданного временного интервала. Периодическое событие возникает каждый раз после истечения интервала, и фактически представляет собой однократное событие с "перезарядкой".

Уведомление о наступлении событий

Подсистема таймера поддерживает только один способ уведомления о наступлении таймерного события - вызов заданной программной функции. Для вызова функции подсистемой создается отдельная задача (thread) с приоритетом TIME_CRITICAL, из которой и вызывается заданная функция. Однажды созданная задача продолжает существовать до завершения приложения, обслуживая все последующие таймерные события.

Точность

Под точностью (accuracy) понимается способность таймерной подсистемы вызвать при уведомлении заданную программную функцию как можно ближе к моменту наступления события. Точность вызова увеличивется с повышением разрешения таймера, однако, поскольку вызов функции выполняется вспомогательной задачей, имеет место задержка на диспетчеризацию самой задачи. По умолчанию точность составляет примерно 7-10 мс, при разрешении 1 мс - 0-1 мс. При нехватке памяти, когда активизируется процесс подкачки (swapping) точность может резко падать до нескольких десятков миллисекунд.

Поскольку ни одна платформа Win32 не является системой реального времени, нет никаких гарантий точности работы таймера. Даже высший уровень приоритета вызывающей задачи не гарантирует от задержек на срочные системные операции, длительность которых в системах Windows никак не регламентируется.

Общая схема взаимодействия программы и подсистем

Работа с джойстиком обычно начинается с получения количества устройств функцией joyGetNumDevs, хотя некоторые программы всегда работают с первым устройством (нулевой номер). При помощи функции jouGetDevCaps можно получить характеристики выбранного джойстика, в частности - количество и конфигурацию его осей.

Если программа поддерживает собственные регулярные таймерные события - она может просто периодически опрашивать состояние джойстика функциями joyGetPos / joyGetPosEx. Первая пригодна лишь для работы с традиционным джойстиком, вторая - для джойстика любого типа.

При необходимости получать сообщения об изменении состояния джойстика необходимо захватить его функцией joySetCapture, указав ключ окна (window handle), которое будет получать уведомляющие сообщения. Попутно функцией joySetThreshold можно задать порог чувствительности, чтобы сообщения посылались в "прореженном" виде - не на каждую единицу перемещения, а на каждую вторую, третью и т.п. Освобождение джойстика от захвата выполняется функцией joyReleaseCapture.

Работа с таймером обычно начинается с обращения к функции timeGetDevCaps для получения параметров разрешения, чтобы согласовать требуемое программе разрешение с возможностями аппаратуры и платформы.

Блоки программы, требующие работы с повышенным разрешением таймера, обрамляются вызовами функций timeBeginPeriod и timeEndPeriod; такие блоки могут вкладываться друг в друга (подсистема поддерживает стек, в котором сохраняются параметры разрешения), однако каждый вызов timeBeginPeriod должен быть впоследствии закрыт соответствующим ему вызовом timeEndPeriod.

Для запроса таймерного события применяется функция timeSetEvent, которая "заводит" таймер на заданный временной интервал. В зависимости от параметров, результатом функции будет либо однократное событие, либо серия периодических событий, следующих друг за другом через заданный промежуток времени.

При необходимости досрочно отменить запрошенное событие используется функция timeKillEvent. При этом необходимо иметь в виду, что вызов функции уведомления происходит из независимой задачи, и уведомление может произойти в тот же момент, что и отмена события. Для исключения коллизий в таких случаях нужно использовать механизмы синхронизации процессов.

Независимо от таймерных событий, приложение может в любой момент запросить текущее системное время функциями timeGetSystemTime и timeGetTime. Частота изменения, или гранулярность, системного времени зависит от текущего разрешения таймера. Установленное разрешение таймера влияет также и на результат функции GetTickCount и ее производных.

При запросе таймерных событий с высокой (сотни раз в секунду) частотой имеет смысл принять меры к минимизации накладных расходов и увеличению точности отслеживания времени. Например - уменьшить до предела объем кода в функции уведомления, зафиксировать код функции и используемые им данные в памяти посредством функции VirtualLock, и т.п.

Средства разработки, включаемые файлы и библиотеки

Как и ранее, описывается программирование на языке C/C++ в среде Microsoft Visual C++.

Все необходимые константы, типы, структуры и прототипы функций подсистемы определяется в файле MMSYSTEM.H, который по умолчанию включается в компиляцию из общего файла WINDOWS.H. Дополнительные, редко используемые константы определены в файле MMREG.H.

Интерфейсные функции импортируются из библиотеки WINMM.LIB.

Структуры, используемые в интерфейсе

Структура JOYCAPS

Описывает свойства и характеристики устройства джойстика. Все поля заполняются только подсистемой и драйвером джойстика.

WORD wMid;
WORD wPid;
CHAR szPname [MAXPNAMELEN];
UINT wXmin;
UINT wXmax;
UINT wYmin;
UINT wYmax;
UINT wZmin;
UINT wZmax;
UINT wNumButtons;
UINT wPeriodMin;
UINT wPeriodMax;
UINT wRmin;
UINT wRmax;
UINT wUmin;
UINT wUmax;
UINT wVmin;
UINT wVmax;
UINT wCaps;
UINT wMaxAxes;
UINT wNumAxes;
UINT wMaxButtons;
CHAR szRegKey [MAXPNAMELEN];
CHAR szOEMVxD [MAXOEMVXD];
HASZ Имеет ось Z.
HASR Имеет ось R (руль).
HASU Имеет ось U.
HASV Имеет ось V.
HASPOV Имеет манипулятор POV.
POV4DIR Манипулятор POV поддерживает только дискретные направления (нейтраль, вперед, вправо, назад, влево).
POVCTS Манипулятор POV поддерживает плавное изменение направления.

Структура JOYINFO

Описывает состояние традиционного джойстика (до трех осей и до четырех кнопок). Введена в Win16, может использоваться в Win32, если информации достаточно для описания состояния опрашиваемого джойстика. Заполняется подсистемой.

UINT wXpos;
UINT wYpos;
UINT wZpos;
UINT wButtons;

Структура JOYINFOEX

Описывает состояние любого поддерживаемого в Win32 джойстика (до 6 осей и до 32 кнопок). Заполняется подсистемой, кроме полей dwSize и dwFlags.

DWORD dwSize;
DWORD dwFlags;
DWORD dwXpos;
DWORD dwYpos;
DWORD dwZpos;
DWORD dwRpos;
DWORD dwUpos;
DWORD dwVpos;
DWORD dwButtons;
DWORD dwButtonNumber;
DWORD dwPOV;
DWORD dwReserved1;
DWORD dwReserved2;

Флаги запрашиваемых значений (префикс JOY_RETURN в именах констант)

X Запрашивается координата X в поле dwXpos.
Y Запрашивается координата Y в поле dwYpos.
Z Запрашивается координата Z в поле dwZpos.
R Запрашивается координата R в поле dwRpos.
U Запрашивается координата U в поле dwUpos.
V Запрашивается координата V в поле dwVpos.
POV Запрашивается значение Point of View в поле dwPow (в дискретных единицах).
POVCTS Запрашивается значение Point of View в поле dwPow (в единицах по 0.1 градуса).
ALL Запрашиваются все возможные значения координат.
CENTERED За центральное положение манипулятора принимается среднее значение координат по каждой из осей.
RAWDATA Запрашиваются сырые (некалиброванные) значения координат.
BUTTONS Запрашивается состояние кнопок в поле dwButtons.

Вспомогательные флаги (префикс JOY_):

USEDEADZONE Создание вблизи центрального значения координат "мертвой зоны", внутри которой возвращается одно и то же (среднее) значение координаты. Для того, чтобы значение начало изменяться, необходим вывод манипулятора за пределы зоны. Может использоваться для подавления дребезга в области нейтрального положения.

Флаги калибровки (префикс JOY_CAL_):

READ3 Запрос некалиброванных значений X, Y, Z.
READ4 Запрос некалиброванных значений X, Y, Z, R.
READ5 Запрос некалиброванных значений X, Y, Z, R, U.
READ6 Запрос некалиброванных значений X, Y, Z, R, U, V.
READXONLY Запрос некалиброванного значения X.
READYONLY Запрос некалиброванного значения Y.
READXYONLY Запрос некалиброванных значений X, Y.
READZONLY Запрос некалиброванного значения Z.
READRONLY Запрос некалиброванного значения R.
READUONLY Запрос некалиброванного значения U.
READVONLY Запрос некалиброванного значения V.
READALWAYS Обращаться к порту состояния джойстика, даже если драйвер обнаружил, что джойстик не подключен.

Назначение флагов вида JOY_CAL_xxx из документации непонятно. Судя по тексту драйвера, эти флаги запрашивают отдельные некалиброванные значения, однако на практике наличие любого из этих флагов приводит к полному обнулению результирующей структуры.

Структура TIMECAPS

Описывает параметры мультимедийного таймера.

UINT wPeriodMin;
UINT wPeriodMax;

Структура MMTIME

Описывает время в терминах различных мультимедийных потоков. Используется для получения текущего времени функцией timeGetSystemTime, заполняется подсистемой.

UINT wType;
union {
  DWORD ms;
  DWORD sample;
  DWORD cb;
  DWORD ticks;
  struct {
    BYTE hour;
    BYTE min;
    BYTE sec;
    BYTE frame;
    BYTE fps;
    BYTE dummy;
    BYTE pad [2]
  } smpte;
  struct {
    DWORD songptrpos;
  } midi;
} u;
BYTES Количество байтов от начала мультимедийного потока
MIDI Время в стандарте MIDI (Sond Position Pointer)
MS Время в миллисекундах
SAMPLES Количество звуковых блоков с начала звукового (Audio) потока
SMPTE Время в стандарте SMPTE (в кадрах)
TICKS Время в тиках от начала MIDI–потока

Уведомления, передаваемые программе

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

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

Уведомление о состоянии джойстика

Для уведомления о состоянии джойстика используются сообщения, отражающие произошедшие изменения. Константы для кодов сообщений имеют префикс MM_JOY:

Вместо n в именах присутствует цифра 1 или 2, обозначающая номер джойстика. Во всех сообщениях параметр wParam содержит битовую шкалу нажатых кнопок. Параметр lParam представляет значения координат: в сообщениях ZMOVE - Z (младшее слово), в остальных сообщения - X (младшее слово) и Y (старшее слово). Для выделения координат из двойного слова удобно использовать макросы LOWORD и HIWORD.

Как видно, в подсистеме предусмотрены сообщения лишь для трехосевых джойстиков. Об изменении координат R, U, V и POV приложению никак не сообщается - для этого необходимо организовать периодический опрос собственными силами - например, по периодическому таймерному событию.

Уведомление о наступлении таймерного события

При наступлении таймерного события подсистема вызывает заданную программную функцию из специально созданной вспомогательной задачи (thread), имеющей уровень приоритета TIME_CRITICAL. Однажды созданная вспомогательная задача существует до завершения процесса; с ее помощью также выполняются все последующие отработки возникающих таймерных событий.

Набор интерфейсных функций подсистем

В отличие от ранее описанных подсистем MME, интерфейсы джойстика и таймера достаточно своеобразны и не допускают какой-либо систематизации набора функций. Поэтому все имена функций приводятся полностью, без сокращений.

Перечень интерфейсных функций

joyGetNumDevs Запрос количества устройств джойстика
joyGetDevCaps Запрос параметров и возможностей устройства джойстика
joyGetPos Запрос состояния традиционного джойстика
joyGetPosEx Запрос состояния любого джойстика
joyGetThreshold Запрос порога чувствительности
joySetThreshold Установка порога чувствительности
joySetCapture Захват джойстика
joyReleaseCapture Освобождение джойстика от захвата
timeGetDevCaps Запрос параметров таймера
timeGetSystemTime Запрос системного времени в виде структуры MMTIME
timeGetTime Запрос системного времени в миллисекундах
timeBeginPeriod Начало работы с заданным разрешением
timeEndPeriod Конец работы с заданным разрешением
timeSetEvent Запрос таймерного события
timeKillEvent Отмена таймерного события
CallbackProc Прототип функции уведомления таймера

Значения, возвращаемые интерфейсными функциями

За редким исключением, все функции интерфейса возвращают результат типа MMRESULT, эквивалентный типу UINT. Значение MMSYSERR_NOERROR, равное нулю, означает успешное выполнение функции, любое другое значение указывает на ошибку. Константы для кодов ошибок имеют префиксы MMSYSERR_ (общая ошибка мультимедийной подсистемы), JOYERR_ (ошибка подсистемы джойстика) и TIMERR_ (ошибка подсистемы таймера):

MMSYSERR_BADDEVICEID Недопустимый номер устройства
MMSYSERR_NOTENABLED Драйвер не активизирован
MMSYSERR_NODRIVER Драйвер отсутствует
MMSYSERR_NOMEM Недостаточно памяти
MMSYSERR_NOTSUPPORTED Запрошенная функция не поддерживается
MMSYSERR_INVALFLAG Недопустимый флаг
MMSYSERR_INVALPARAM Недопустимый параметр
MMSYSERR_ERROR Неопределенная ошибка
JOYERR_NOERROR Успешное завершение запроса
JOYERR_PARMS Недопустимые параметры запроса
JOYERR_NOCANDO Невозможно выполнить запрос
JOYERR_UNPLUGGED Джойстик не подключен
TIMERR_NOERROR Успешное завершение запроса
TIMERR_NOCANDO Невозможно выполнить запрос
TIMERR_STRUCT Недопустимый размер структуры

Описание интерфейсных функций джойстика

joyGetNumDevs - запрос количества устройств

UINT joyGetNumDevs (void);

Возвращает количество установленных в системе устройств джойстика.

joyGetDevCaps - запрос параметров и возможностей джойстика

MMRESULT joyGetDevCaps (
  UINT DevNum,
  JOYCAPS *Caps,
  UINT CapsSize
);

Служит для определения параметров и возможностей джойстика.

При успешном завершении функция заполняет поля переданной указателем структуры параметрами устройства.

joyGetPos - запрос текущего состояния традиционного джойстика

MMRESULT joyGetPos (
  UINT DevNum,
  JOYINFO *Info
);

joyGetPosEx - запрос текущего состояния любого джойстика

MMRESULT joyGetPosEx (
  UINT DevNum,
  JOYINFOEX *Info
);

Перед обращением к функции необходимо заполнить поля dwSize и dwFlags передаваемой структуры. Первое отражает версию программного интерфейса, а второе - режимы выполнения запроса и набор возвращаемых подсистемой параметров джойстика.

joyGetThreshold - запрос порога чувствительности к перемещению

MMRESULT joyGetThreshold (
  UINT DevNum,
  UINT *Threshold
);

joySetThreshold - установка порога чувствительности к перемещению

MMRESULT joySetThreshold (
  UINT DevNum,
  UINT Threshold
);

joySetCapture - захват джойстика

MMRESULT joySetCapture (
  HWND Win,
  UINT DevNum,
  UINT Period,
  BOOL OnChange
);

joyReleaseCapture - освобождение джойстика

MMRESULT joyReleaseCapture (
  UINT DevNum
);

Освобождает джойстик от ранее установленного режима захвата, выполненного функцией joySetCapture, прекращая посылку уведомляющих сообщений и делая джойстик доступным для нового захвата.

Описание интерфейсных функций таймера

timeGetDevCaps - запрос параметров и возможностей таймера

MMRESULT timeGetDevCaps (
  TIMECAPS *Caps,
  UINT CapsSize
);

Служит для определения параметров таймера - минимального и максимального поддерживаемого разрешения.

При успешном завершении функция заполняет поля переданной указателем структуры параметрами разрешения.

timeGetSystemTime - запрос системного времени в виде структуры

MMRESULT timeGetSystemTime (
  MMTIME *Time,
  UINT StructSize
);

При успешном завершении функция заносит в поле ms значение системного времени в миллисекундах с момента загрузки системы. Поле wType функцией не рассматривается, и после выполнения запроса всегда устанавливается в значение TIME_MS.

timeGetTime - запрос системного времени в миллисекундах

DWORD timeGetTime(VOID);

Возвращает значение системного времени в миллисекундах с момента загрузки системы.

timeBeginPeriod - начало работы с заданным разрешением

MMRESULT timeBeginPeriod (
  UINT Period
);

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

Частота обновления системного времени, получаемого функциями timeGetSystemTime, timeGetTime, GetTickCount и им подобными, определяется частотой аппаратного системного таймера.

Каждый вызов timeBeginPeriod должен быть впоследствии "закрыт" соответствующим вызовом timeEndPeriod, обозначающим блока программы, работающего с заданным разрешением.

timeEndPeriod - конец работы с заданным разрешением

MMRESULT timeEndPeriod (
  UINT Period
);

Функция завершает работу с указанным разрешением таймера, восстанавливая предыдущее разрешение.

timeSetEvent - запрос таймерного события

UINT timeSetEvent (
  UINT Delay,
  UINT Resolution,
  TIMECALLBACK *TimeProc,
  DWORD UserData,
  UINT EventType
);

Функция "заряжает" виртуальный таймер на заданный интервал времени, возвращая идентификатор будущего таймерного события (виртуального таймера). В случае неудачи возвращается нулевое значение. Идентификатор события передается также функции уведомления в ее вызове при наступлении события.

Однократное событие возникает один раз после истечения указанного интервала, периодическое - через каждый промежуток времени заданной величины. Для досрочной отмены однократного события или для прекращения периодических событий необходимо использовать функцию timeKillEvent.

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

timeKillEvent - отмена таймерного события

MMRESULT timeKillEvent (
  UINT EventId
);

Функция отменяет таймерное событие, которое еще не произошло, а также отменяет регулярную генерацию периодического события.

Вход в функцию еще не гарантирует отмены события. Если событие наступает после входа в функцию, но до достижения точки блокировки переключения задач, вспомогательная задача может успеть получить управление и выполнить вызов функции уведомления.

При попытке отмены уже отработанного события возвращается ошибка MMSYSERR_INVALPARAM, больше никаких действий не выполняется. Однако, если с момента отработки события до вызова timeKillEvent выполняется функция timeSetEvent, данный идентификатор события может быть назначен повторно, вследствие чего может быть ошибочно отменен. Поэтому при работе с событиями необходимо уделять пристальное внимание корректному пути выполнения блоков программы, используя системные средства синхронизации процессов.

TimerProc - функция приложения, вызываемая при уведомлении

Эта функция определяется приложением, подсистема таймера вызывает ее при выполнении уведомлений, передавая в ее аргументах параметры события. Прототип функции имеет следующий вид (имя CallbackProc приведено для примера, реальное имя выбирается приложением):

        void CALLBACK CallbackProc (
  UINT EventId,
  UINT Msg,	
  DWORD UserData,
  DWORD Param1,
  DWORD Param2
);

В Win16 функция может вызываться в контексте обработчика прерывания, поэтому безопасно может использовать лишь ограниченный набор функций Windows: EnterCriticalSection, LeaveCriticalSection, midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage, PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent, timeSetEvent. Обращение к другим системным функциям, как и к функциям подсистемы, может вызвать непредсказуемые последствия.

В Win32 вызов функции выполняется из отдельной задачи, поэтому никаких ограничений на выполняемые действия не накладывается. Однако нужно помнить, что вспомогательная задача имеет приоритет TIME_CRITICAL, и выполнение в ее контексте длительных операций может вызвать торможение остальных задач процесса и/или системы.

Вспомогательная задача создается один раз и существует до полного завершения процесса. Подсистема вызывает из этой задачи функции уведомления для всех событий, которые будут запрошены за время жизни процесса.

Параметры Msg, Param1 и Param2 сохранены только в целях унификации прототипа - все подсистемы MME для удобства используют для функций уведомления один и тот же прототип.

Недостатки подсистем джойстика и таймера

Так же, как и подсистемы Wave/MIDI, подсистемы джойстика и таймера в Windows 95/98 остались 16-разрядными, как и в Windows 3.x. Из-за этого каждое обращение к ним из Win32–приложения сопровождается двойной сменой режима исполнения (thunking), приводящее, к дополнительным накладным расходам. Тем не менее, использование мультимедийного таймера - единственный способ достичь более высокого временнОго разрешения по сравнению со стандартным.

В Windows NT/2000 все подсистемы сделаны изначально 32-разрядными, так что описанных проблем там не возникает.

Нетрадиционные применения интерфейса джойстика

Интерфейс джойстика может использоваться не только для подключения манипуляторов. Он может рассматриваться, как универсальный входной порт с четырьмя аналоговыми и четырьмя цифровыми входами.

Схемотехника аналоговых входов ориентирована на измерение сопротивлений 0..100 кОм, включенных между входом и питающим напряжением +5 В, путем измерения протекающего через вход тока. Следовательно, интерфейс может быть использован для измерения любых аналоговых величин, значения которых преобразованы в пропорциональные им токи соответствующих величин - освещенности, температуры, влажности и т.п. Нельзя только забывать о значительной погрешности метода, которая делает его непригодным для точных измерений.

Цифровые входы интерфейса могут подключаться к любым цепям, обеспечивающим замыкание входа на землю - герконам, реле, переключателям, транзисторам и выходам микросхем ТТЛ с открытым коллектором, электронным ключам и т.п. Например, при наличии источника бесперебойного питания (UPS) с простым интерфейсом, выдающим только сигнал перехода на батарейное питание, этот сигнал может быть принят через интерфейс джойстика специально написанной для этого программой, которая может инициировать нормальное завершение работы системы до того, как батарея UPS полностью разрядится и питание системы будет выключено аварийно.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 0    Оценка 45        Оценить