СХЕМЫ И ДОКУМЕНТАЦИЯ

Советы / Программирование портов ввода/вывода (LPT и ISA).


<<< К списку раздела.

Программирование портов ввода/вывода (LPT и ISA).

Данный материал взят с: http://students.soros.karelia.ru/~eugenes/

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

В повседневной практике я использую язык программирования "Паскаль", поэтому дальнейшие примеры буду давать на нём. Согласен, для написания "драйверов" лучше знать СИ и Ассемблер. Последний я на данный момент изучаю. Для моих устройств пока что хватает и Паскаля.

Работа с параллельным портом (LPT).

Обычно этот порт имеет базовый адрес 378h (LPT1), 278h (LPT2), 3BCh (LPT3). Окончание "h" свидетельствует о шестнадцатиричной системе счисления, так уж принято записывать адреса. Порт занимает три адреса, первый из них называется базовым. Так, для LPT1 диапазон занимаемых им адресов: 378h-37Ah. Базовый адрес служит для посылки/чтения байта на/из линии d0-d7 (пины 2-9 разъёма DB-25). Посылка не инвертируется. Приведу фрагмент программы, посылающей в порт число 170.

{начало программы}
...
begin
Port[$378]:=170
end.
{конец программы}

При запуске её на линиях d0-d7 появится число 170 в двоичном виде, что соответсвует 10101010. Т.е. единичный сигнал будет присутствовать на выводах d1, d3, d5, d7 (обозначени выводов начинается с d0!). Число 170 останется на выводах разъёма до тех пор, пока Вы не перешлёте туда же другое число (это может сделать и другая программа) или не выключите компьютер. Заметьте, что адрес порта в команде задан в шестнадцатиричном виде, а посылка - в десятиричном. Если вместо команды
Port[$378]:=170;
Вы примените
d:=Port[$378];
где d - некоторая переменная, то переменная примет значение последнего посланного в порт байта или, при переходе в режим приёма, значение байта, поданного на порт внешним устройством.

Базовый+1 адрес (379h для LPT1) служит для чтения состояний принтера, поступающих на входы ACK, -BUSY, PE, SLCT, ERROR. Сигнал -BUSY - инвертированныё, т.е. при подаче на него +5В компьютер будет считывать "0". Для опроса линий используются только старшие 5 битов. "1" в третьем бите соответствует высокому уровню сигнала на входе ERROR. В четвёртом бите она индицирует о высоком уровне сигнала на входе SLCT, в пятом - на входе PE. Единица в шестом бите соответствует высокому уровню сигнала на ACK, а ноль в седьмом - выскому уровню на -BUSY. Если ваш компьютер имеет однонаправленный порт передачи данных, то эти пять линий предоставляют единственную возможность в опросе состояний внешних датчиков.

Ниже - пример программы, опрашивающей входные линии порта:
begin
d:=Port[$379]
end.

В переменной d после выполнения программы будет отображено состояние порта. Допустим, переменная вернула значение 126. В двоичном виде оно выглядит как 01111110. Младшие (правые) три бита (нулевой, первый и второй) не используются, и всегда равны 1, 1 и 0. Третий бит - 1, значит на ERROR высокий уровень. Та же ситуация на SLCT, PE, ACK и BUSY.

Базовый+2 адрес (37Ah) служит для записи битов на линии -STROBE, -AUTO FD, INIT, -SLCT IN. Нулевой бит посылает сигнал на -STROBE, первый - на -AUTO FD, второй - на INIT, и четвёртый - на -SLCT IN. Принцип записи - тот же, что и по базовому адресу. Нам же очень интересны следующие биты: Пятый бит служит для разрешения/запрещения прерывания от внешнего устройства. Это полезно, если Вы умеете писать обработчики прерываний.

Шестой бит служит для перевода линий d0-d7 в режим приёма!!! Но перед этим необходимо убедиться, что в BIOS в типе порта поставлено SPP/EPP. Вот пример программы, которая считывает бит с линий данных:
begin
Port[$37A]:=32; {32 "зажигает" единицу в шестом бите}
d:=Port[$378];
end.

Не забудьте, что если Вы собрали устройство, которое через порт принтера опрашивает, допустим, один датчик, подключенный к d0 (pin 2), а остальные линии просто оставили "висеть" на воздухе, то в случае, когда на втором контакте порта будет единица, принимаемый байт будет не "1" а 255, т.к. неподключенные контакты имеют высокий уровень.

Программирование ISA устройств.

Программирование портов ISA практически не отличается от программирование порта LPT, т.к. по сути дела сам порт принтера является ISA устройством. Отличие состоит только в том, что при работе с параллельним портом можно было обходится без прерываний (хотя при разработке устройств, обрабатывающих данные в "real time" без них не обойтись). Если Вы решили собирать ISA устройство, то очевидно, что Вам как раз и нужна обработка в реальном времени или быстрый (по сравнению с LPT) обмен данными. К сожалению, я пока что не имел опыта разработки драйверов, используюих прервания, поэтому на данный момент не смогу Вам объяснить методы написания таковых.

Возъмём простейший случай, когда Вы решили использовать шину ISA только для достижения 16-разрядного обмена. Если адрес LPT порта (в общем случае) является уже заданным, то Ваше устройство может использовать любые адреса (естественно, не занятые другими устройствами). Могу дать несколько советов при выборе адреса, который будет занимать устройство:

  • Тщательно проверьте, не будет ли оно конфликтовать с другими устройствами. Это может быть не только видеокарта, модем или звуковая карта, но и что-либо из внутренних устройств на материнской плате. Поиск свободных адресов можно осуществить в простейшем случае из Windows (Свойства: система -> Устройства. Двойной щелчок по пиктограмме "Компьютер" высветит меню, в котором можно будет просмотреть адреса).
  • Для подстраховки и удобства переноса устрйства на другой компьютер лучше сделать возможность выбора разных адресов джамперами (если Вы сможете организовать Plug&Play, тогда преклоняю свои колени..).
  • Старайтесь не занимать верхних адресов, т.к. они могут попросту не выводится на шину. Держитесь в окресностях адресного пространства, которое занимают другие ISA устройства.
  • Не забывайте выводить старшие линии адресов на своё устройство, т.к. может оказаться, что оно будет совпадать с другими устройствами младшими битами адреса.

    Если Вы сделали выборку адресов джамперами, то программируя на Паскале трудно будет программно изменить адрес устройства (поправьте, если я не прав). Будет гораздо проще применить Ассемблер-вставку в Вашу программу:
    mov dx, a
    mov al, d
    out dx, al

    Здесь в регистр dx мы кладём адрес порта, а в al - байт для отправки. Переменные a и d мы может спокойно передавать вставке из программы.


    Разработка и оформление Андрея Александровича Борисенко aka ICE.
    По всем вопросам просьба писать мне на icenet (at) narod.ru