ПРОГРАММИРОВАНИЕ ПОРТА LPT LPT-PORT.NAROD.RU |
|
Чтение/запись в LPT порт (Часть 1)Итак, настало время написать простую программу, иллюстрирующую приемы чтения и записи данных в LPT порт. Пока напишем ее в консольном варианте, дабы на этапе понимания и разбора этой программы не пришлось копаться в дебрях кода под Windows (не переживайте, следующая статья будет посвящена как раз приложению по Win).
Прежде чем двигаться дальше и писать программу, необходимо разобраться с LPT портом, посмотреть из чего он состоит и как нам воспользоваться им в своих целях. Внутренности LPT порта Если говорить на бытовом уровне, то можно сказать, что LPT порт это набор контактов, на которых мы можем установить напряжение 0 или +5 В (логическая 0 и 1) из программы или это может сделать внешнее устройство снаружи. Давайте разберемся, какими контактами мы можем оперировать, а какими нет. В этом нам поможет рис. ниже (его рисовал не я, автор мне неизвестен. Но он уж больно хорош, я и сам им постоянно пользуюсь) Из рис. видно, что выводы порта можно разделить на четыре группы: это 'земляные' выводы (не понятно чем руководствовались разработчики интерфейса LPT, сделав этих выводов аж 8 штук). Они обозначены черным цветом (контакты 18-25). Все они соеденены между собой, поэтому для своих разработок в качестве земли можно использовать любой из них. Красным цветом обозначены выводы так называемого регистра Data (контакты 2-9). Под регистром будем понимать (на бытовом уровне) объдинение группы контактов LPT порта. В регистре Data их 8 штук. Это самый толковый регистр - он позволяет нам как из программы, так и из внешнего устройства установить на его контактах лигическую 0 или 1, т.е. он двунаправленный. Именно его мы и использовали в нашей первой программе Port.exe - подключали светодиод ко 2-му выводу порта (как теперь понятно, этот вывод принадлежит регистру Data, является его нулевым битом) и 25 выводу (земля), и спомощью программы управляли подачей напряжения на вывод 2 относительно земли. Чтобы обращаться к этому регистру, надо знать его адрес: 0x378 - в 16-ричной системе или 888 в десятичной (на рис. написано &H378 - это тоже самое что и 0x378, просто первое обозначение присуще языку Pasсal и ему подобным, мы же пишем на Си). Опять вспоминая программу Port.exe, заметим, что обращались мы к регистру с помощью следующей функции _outp(Address, 0);, егде переменная Address была предварительно определена как 888. Теперь понятно, что этим мы указывали функции _outp, что мы хотим работать именно с регистром Data. Продолжим рассмотрение порта. Осталось еще два регистра. Следующим будет регистр Status (контакты 10-13, 15). Это однонаправленный регистр. Управлять им можно только из снаружи, через внешнее устройство (имеется в виду изменять данные на нем, читать можно из любого регистра в любую строну). Он имеет адрес 0x379 - в 16-ричной системе или 889 в десятичной. И регистр Control (контакты 1, 14, 16-17). Он имеет всего 4 контакта и может управляться только программой. Его адрес: 890 в десятичной системе.
Теперь рассмотрим, а как происходит запись и чтение данных в регистры LPT порта, т.е. как нам установить на нужных выводах 0 или 1. Запись/чтение данных в регистр Data Итак, рассмотрим сразу практическую задачу. Хочу чтобы на выводе регистра Data под номером 3 (3 - это номера вывода LPT порта) была установлена логическая 1 (т.е. чтоб между ним и землей было +5 В) и на остальных выводах этого регистра (2,4-9 выводы порта) были нули. Пишем код: int Address=888; int data=2; Out32(Address, data); Я использовал функцию Out32 библиотеки inpout32.dll, будем привыкать к ней, т.к. дальнейшие примеры будем разбирать именно на этой библиотеке. Если этот код выполнить, то получится что на выводе порта 3 есть +5 В, а на 2,4-9 висит ноль. Как это получилось? Начнем разбираться: первым параметром функции Out32 мы передаем число 888. Как Вы уже знаете, это адрес регистра Data LPT порта. Теперь функция знает куда ей писать данные. Далее вторым параметром мы передаем число 2. Прошу обратить внимание, что двоика в десятичной системе счисления. Что дальше делает функция? Ей надо эту двойку запихнуть в регистр Data, но вот проблема: регистр совершенно непонимает что такое 2. Он знает 0 или 1. Больше ничего. Тогда функция как бы "переводит эту двоику в двоичную систему счисления" (это не совсем верно, но для объяснения на пальцах сгодится) и каждый разряд двоичного числа справо на лево записывает по порядку в регистр начиная с младшего разряда D0 (вывод 2 порта) и заканчивая старшим D7 (вывод 9). Если Вы переведете число 2 из десятичной в двоичную систему счисления то получите 10. Функция берет первый разряд двоичного числа - это 0 (самую правую) и пишет ее в D0, далее берет 1 и пишет ее в D1. Т.к. регистр 8-ми разрядный (у него есть 8 контактов), функция продолжает брать данные справо на лево и писать в следующий бит регистра. Т.к. наше число закончилось, то функция как бы дописывает нулями наше двоичное число слева, чтоб оно стало 8-ми разрядным. Эта опрерация смыслу не противоречит, т.к. например, что 23 руб. так и 00023 руб. - одно и тоже. Ну что, мозг опух пока прочитали? Сейчас станет понятнее. Давайте в регистр Data запишем число 245. Пишем код: int Address=888; int data=245; Out32(Address, data); Опять переводим 245 в двоичную систему счисления и справо на лево записываем разряды числа в соответсвующие биты регистра. В итоге получим, что на выводах LPT порта под номерами 2,4,6-9 присутствует напряжение +5 В, на выводах 3,5 ноль. Ну что, теперь я думаю, с записью данных в регистр Data мы разобрались. Надо отметить, что диапозон десятичных чисел, которые можно записать в регистр Data лежит в пределах от 0 до 255. Регистр он у нас 8-ми разрядный, значит максимальное число комбинации 0 и 1 на его выводах составляет 28-1=256-1=255. Чтение данных Теперь давайте получим данные из порта, а именно из его регистра Data, к нам в программу. Мы хоти узнать, на каких выводах регистра Data сейчас высокий уровень напряжения, а на каких низкий. Помните, выше мы записали в порт число 245? Давайте его сейчас получим из порта. Пишем код: int Address=888; int data; data = Inp32(Address); Inp32 это функция для чтения данных из порта библиотеки inpout32.dll. Единственным параметром для нее является адрес того регистра, откуда мы хотим прочесть данные. На выходе она возвращает десятичное число, соответствующее текущему содержомому регистра. Выполнив этот код, переменная data будет содержать число 245. Что это значит? Чтобы разобраться, переводим число 245 из десятичной в двоичную и смело можем сказать что на выводах порта 2,4,6-9 сейчас +5 В а на выводах 3,5 0 В. (см. рис. выше) Как я уже упомянул выше, в регистр Data данные записать может и внешнее устройство. Однако рассмотрение этого вопроса пока оставим, т.к. это потребует внешних источников питания. Сначала, давайте полностью разберемся с базовыми операциями. Запись/чтение данных в регистр Control Теперь поуправляем регистром Control. Он однонаправленный, данные в него может записать только наша программа. Обратите внимание на несколько особенностей этого регистра. Во-первых, о содержит всего четыре рабочих вывода. Значит в него можно записать число в диапозоне от 0 до 24-1=16-1=15. Во-вторых, он имеет очень непрятную особенность: некоторые из его выводов инвертированы, т.е. если Вы на этот вывод пишете 1, то на ней устанавливается 0. И наоборот, читаете 1, а на самом деле там 0. Поэтому, значение записываемых данных и читаемых данные не совсем очевидны. Для разбора необходимо просто попробовать писать в регистр разные данные и смотреть, что получается. Приведу пример записи числа в регистр Control. Пишем код: int Address=890; //адрес регистра Control int data=10; Out32(Address, data); И пример чтения: int Address=890; //адрес регистра Control int data; data = Inp32(Address); Запись/чтение данных в регистр Status Наконец, добрались до регистра Status. Он однонаправленный, данные в него может записать только внешнее устройство, т.е. мы в программе можем только читать содержимое этого регистра. Причитав данные из Status, и переведя их в двоичное число, сразу довольно трудно понять что же реально творится с напряжениями на выходах этого регистра. Во-первых, он тоже имеет инвертированные выводы, а во-вторых рабочими являются биты под номерами 4-7, а 0-3 не используются, и следовательно число записывается довольно хитро. Чтобы разобраться, лучше просто несколько раз делать чтение при разных данных в регистре. Возникает вопрос, а как эти данные на нем установить? Довольно просто. В качестве внешнего устройства, пока, будете выступать Вы. Выполните такой код. int Address=889; //адрес регистра Status int data; data = Inp32(Address); Вы получите некоторое число. Теперь возмите проводник и соедините им любой из земляных выводов порта (18-25) с каким-нибудь выводом регистра Status (10-13, 15), например с десятым. И снова выполните чтение. Вы получите другое число. Уберите проводник. Прочитав, получете исходное число. Как это работает? Исходно, на всех выводах этого регистра находится высокий уровень напряжения +5 В. Когда мы соеденили один из его выводов с землей, то на нем, соответственно, напряжение стало равным нулю, т.е. логический ноль. Теперь двоичным данным в регистре соответствует другое десятичное число. Можно попробовать замыкать и другие выводы регистра Status на землю, замыкать сразу несколько. Каждый раз при чтении получится разный результат. Следует заметить, что при таких опытах с регистром Status возникает не совсем понятная ситуация с другими выводами порта LPT. После первого замыкания выводов Status, начинают мигать выводы Data и Control. Это связано с тем, что порт LPT предназначен для подключения принтера, а выводы Status он использует, для того чтобы сообщить компьютеру некоторую служебную информацию. Изменения на выводах Status регестрирует системный драйвер операционной системы. Он же проводит и ответные действия, для нас наблюдаемые в виде периодического изменения состояния других выводов. Тут уж ни чего не поделаешь. Я обычно, просто в начале работы с портом далаю замыкание какой-нибудь линии регистра Status на землю и жду примерно минуту, пока драйвер не утихомирится. После этого порт свободен, и новые операции над регистром Status не приводят к неконтролируемым процессам в порту. Ну вот мы и познакомилиьс поближе с содержимым порта LPT. Теперь можно писать программу. См. Часть №2 Напомню, если не получается, пишите lpt-port@narod.ru, помогу. Был бы благодарен Вам, если бы Вы смогли найти пару минут и написать, понравился ли материал или нет. |
По всем вопросам просьба обращаться: lpt-port@narod.ru
© 2005 "CONTROLLER SYSTEM". Все права защищены. |
программирование порта lpt, программирование драйверов, создание простого драйвера, пример драйвера, как написать драйвер, драйвера NT, драйвер GiveIO.sys