Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содеожание] [Дальше]

Модемы и факс-модемы. Программирование для MS-DOS и Windows.

© Александр Фролов, Григорий Фролов
Том 16, М.: Диалог-МИФИ, 1993.

5. Асинхронный адаптер

Практически каждый компьютер оборудован хотя бы одним асинхронным последовательным адаптером. Обычно он представляет собой отдельную плату или же расположен прямо на материнской плате компьютера. Его иногда называют асинхронным адаптером RS-232-C, или портом RS-232-C. Асинхронный адаптер обычно содержит несколько COM-портов, через которые к компьютеру можно подключать внешние устройства.

Каждому COM-порту соответствует несколько регистров, через которые программа получает к нему доступ, и определенная линия IRQ для сигнализирования компьютеру об изменении состояния порта. На этапе инициализации модули BIOS присваивают каждому COM-порту уникальный номер. Например, компьютер может иметь четыре порта COM1 - COM4.

В настоящее время порт последовательной передачи данных используется очень широко. Чаще всего к нему подключается манипулятор мышь. Иногда через COM-порт к компьютеру подключаются графопостроители, сканеры, принтеры, дигитайзеры.

Для нас наиболее интересно использование COM-портов для обмена данных между двумя компьютерами через модемы и нуль-модемы.

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

Модем также подключается к компьютеру через COM-порт и позволяет обмениваться данными по обычным телефонным линиям.

5.1. Основные понятия и термины

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

Рис. 5.1. Формат данных, передаваемых через COM-порт

Из рисунка видно, что исходное состояние линии последовательной передачи данных - уровень логической 1. Это состояние линии называют отмеченным - MARK. Когда начинается передача данных, уровень линии переходит в 0. Это состояние линии называют пустым - SPACE. Если линия находится в таком состоянии больше определенного времени, считается, что линия перешла в состояние разрыва связи - BREAK.

Стартовый бит START сигнализирует о начале передачи данных. Далее передаются биты данных, вначале младшие, затем старшие.

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

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

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

Другая важная характеристика - скорость передачи данных. Скорость передачи данных обычно измеряется в битах за секунду (bps) или в бодах (по фамилии французского изобретателя телеграфного аппарата Emile Baudot - Э. Бодо). Боды определяют количество передаваемых битов за секунду. При этом учитываются и старт/стопные биты, а также бит четности.

В литературе иногда неправильно отождествляют термины бит за секунду и боды. Эти термины действительно равнозначны, когда говорят о передаче данных через COM-порт, но при передаче данных по телефонной линии через модем с высокой скоростью значения скорости передачи информации в битах за секунду и бодах могут различаться. Более подробно об этом можно прочитать в главе "Модемы и факс-модемы", разделе "Боды, биты за секунду и символы в секунду".

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

5.2. Аппаратная реализация

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

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

Сердцем последовательного асинхронного адаптера служит микросхема универсального асинхронного приемопередатчика (UART - Universal Asynchronous Receiver Transmitter). Вы можете встретить несколько разновидностей этой микросхемы - Intel 8250, 16450, 16550, 16550A.

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

При передаче байта он записывается в буферный регистр передатчика, откуда затем переписывается в сдвиговый регистр. Затем байт "выдвигается" из сдвигового регистра по битам. Аналогично работает сдвиговый и буферный регистры приемника.

Программа имеет доступ только к буферным регистрам. Копирование информации в сдвиговые регистры и сдвиг данных выполняется микросхемой UART автоматически. Регистры, управляющие асинхронным последовательным портом, будут описаны в следующей главе.

Внешне каждый COM-порт асинхронного последовательного адаптера представлен собственным разъемом. Существует два стандарта на разъемы COM-порта: это DB25 и DB9. Первый разъем имеет 25, а второй 9 выводов. Несмотря на то, что разъем DB25 содержит в два с половиной раза больше выводов, чем DB9, они передают одинаковые сигналы. При необходимости можно приобрести переходник между разъемами DB25 и DB9.

Внутренний модем содержит COM-порт внутри себя, поэтому на плате внутреннего модема вы не обнаружите ни одного разъема COM-порта.

Приведем разводку разъема DB25 со стороны последовательного асинхронного адаптера:

Теперь приведем разводку разъема DB9 со стороны последовательного асинхронного адаптера:

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

Интерфейс RS-232-C определяет обмен между устройствами двух типов: DTE (Data Terminal Equipment - терминальное устройство) и DCE (Data Communication Equipment - устройство связи). В большинстве случаев, но не всегда, компьютер является терминальным устройством. Модемы, принтеры, графопостроители всегда являются устройствами связи. Рассмотрим теперь сигналы интерфейса RS-232-C более подробно.

5.2.1. Сигналы интерфейса RS-232-C

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

Входы TD и RD используются компьютером и модемом по-разному. Компьютер использует вход TD для передачи данных, а вход RD для приема данных. И наоборот, модем использует вход TD для приема, а вход RD для передачи данных.

Поэтому для соединения компьютера и модема выводы их разъемов необходимо соединить напрямую (см. рис. 5.2).

>

Рис. 5.2. Соединение компьютера и модема

Подтверждение связи

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

Компьютер устанавливает сигнал на линии DTR, чтобы показать модему, что он готов к проведению сеанса связи. В ответ модем устанавливает сигнал на линии DSR. Когда модем произвел соединение с другим удаленным модемом, он устанавливает сигнал на линии DCD, чтобы сообщить об этом компьютеру.

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

Управление потоком

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

Когда одно устройство (например, компьютер), пытается передать данные с большей скоростью, чем они могут быть обработаны принимающей системой (модемом), результатом может стать потеря части передаваемых данных. Чтобы предотвратить передачу большего числа данных, чем то, которое может быть обработано, используют управление связью, называемое "управление потоком" (flow-controll handshake).

Стандарт RS-232-C определяет возможность управления потоком только для полудуплексного соединения. Полудуплексным называется соединение, при котором в каждый момент времени данные могут передаваться только в одну сторону.

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

В полудуплексных соединениях компьютер подает сигнал RTS, когда ему надо передать данные. Модем отвечает сигналом по линии CTS, когда он готов, и компьютер начинает передачу данных. До тех пор, пока оба сигнала RTS и CTS не примут активное состояние, только модем может передавать данные.

При дуплексных соединениях сигналы RTS/CTS имеют противоположные значения по сравнению с теми, которые они имели для полудуплексных соединений.

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

Нуль-модем

Соединить компьютер и модем не составляет труда, так как интерфейс RS-232-C как раз для этого и предназначен. Но если вы пожелаете связать вместе два компьютера при помощи такого же кабеля, который вы использовали для связи модема и компьютера, то у вас возникнут проблемы.

Для соединения двух терминальных устройств - двух компьютеров - как минимум необходимо перекрестное соединение линий TR и RD.

Однако в большинстве случаев этого недостаточно, так как для устройств DTE и DCE функции, выполняемые линиями DSR, DTR, DCD, CTS и RTS, асимметричны.

Устройство DTE подает сигнал DTR и ожидает получения сигналов DSR и DCD. В свою очередь, устройство DCE подает сигналы DSR, DCD и ожидает получения сигнала DTR. Таким образом, если вы соедините вместе два устройства DTE кабелем, который вы использовали для соединения устройств DTE и DCE, то они не смогут договориться друг с другом. Не выполнится процесс подтверждения связи.

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

Чтобы решить все эти проблемы, для соединения двух устройств типа DTE используется специальный кабель, в обиходе называемый нуль-модемом.

Имея два разъема и кабель, вы легко можете спаять его самостоятельно, руководствуясь схемами, изображенными на рисунке 5.3.

Нуль-модемный кабель, представленный в левой части рисунка 5.3, на схеме 1, содержит значительно меньше проводов, чем нуль-модемный кабель, изображенный справа. За счет того, что на каждом конце кабеля линии RTS, CTS и DSR, DCD, DTR соединены вместе, процедуры подтверждения связи и управления потоком всегда будут заканчиваться успешно. На больших скоростях это может привести к потере информации, поэтому мы рекомендуем использовать вторую схему.

Рис. 5.3. Нуль-модем

Для полноты картины рассмотрим еще один аспект, связанный с механическим соединением портов RS-232-C. Из-за наличия двух типов разъемов - DB25 и DB9 - часто бывают нужны переходники с одного типа разъемов на другой. Например, вы можете использовать такой переходник для соединения COM-порта компьютера и кабеля нуль-модема, если на компьютере установлен разъем DB25, а кабель оканчивается разъемами DB9.

Схему такого переходника мы приводим на рисунке 5.4.

Рис. 5.4. Переходник для разъемов DB25 и DB9

Заметим, что многие модемы можно настроить таким образом, что они не будут проверять состояние сигналов DTR и RTS. Более подробно эти возможности описаны в разделе "Расширенный набор AT-команд".

5.3. Порты асинхронного адаптера

На этапе инициализации системы, модуль процедуры начальной загрузки BIOS тестирует имеющиеся асинхронные порты RS-232-C и инициализирует их. В зависимости от версии BIOS инициализирует первые два или четыре порта. Их базовые адреса записываются в области данных BIOS начиная с адреса 0040:0000h.

Чтобы просмотреть значения, записанные в области данных BIOS вашего компьютера, можно воспользоваться программой Debug, поставляемой вместе с операционной системой MS-DOS. Таким образом, можно определить, какие COM-порты установлены на компьютере.

Запустите программу Debug. Для этого введите в строке системного приглашения MS-DOS команду DEBUG.EXE. Программа Debug выведет на экран приглашение в виде черточки '-'. Чтобы просмотреть содержимое оперативной памяти по адресу 0040:0000h введите команду d40:0 и нажмите клавишу . На экране появится дамп памяти, начиная с адреса 0040:0000h до 0040:0080h.


-d40:0 0040:0000 F8 03 F8 02 E8 03 00 00-78 03 00 00 00 
00 00 00 ........x....... 0040:0010 61 C6 00 80 02 80 00 20-00 00 38 00 38 00 E0 
50 a...... ..8.8..P

Нас будут интересовать только первые восемь байт из этого дампа. Первые два байта содержат базовый адрес порта COM1. Поменяв местами два этих байта, получаем адрес 3F8h. Следующие два байта содержат адрес порта COM2 - 2F8h, затем COM3 - 3E8h. Два байта, соответствующие порту COM4, содержат нулевые значения. Это означает, что асинхронный последовательный адаптер компьютера не имеет порта COM4 или BIOS компьютера не может или не пытается его обнаружить.

Теперь вы можете завершить работу программы Debug. Для этого введите команду q и нажмите клавишу .

Адреса COM-портов из нашего примера являются своего рода стандартом. Первый адаптер COM1 обычно имеет базовый адрес 3F8h и занимает диапазон адресов от 3F8h до 3FFh. Второй адаптер COM2 имеет базовый адрес 2F8h и занимает адреса 2F8h...2FFh. Третий адаптер COM3 имеет базовый адрес 3E8h и занимает диапазон адресов от 3E8h до 3EFh. Четвертый адаптер COM4 имеет базовый адрес 2E8h и занимает адреса 2E8h...2EFh. Тем не менее, для некоторых компьютеров, например, с шиной MCA - PS/2, адреса COM-портов могут иметь другие значения.

Порты асинхронного адаптера могут вырабатывать прерывания:

  • COM1, COM3 - IRQ4 (соответствует INT 0Ch);
  • COM2, COM4 - IRQ3 (соответствует INT 0Bh).

    Порты COM1 и COM3 асинхронного последовательного адаптера используют линию IRQ4 и вырабатывают прерывание INT 0Ch, а порты COM2 и COM4 используют линию IRQ3 и вырабатывают прерывание INT 0Bh.

    Некоторые платы последовательного асинхронного адаптера позволяют присвоить COM-портам другие линии IRQ, например, IRQ5 или IRQ7.

    Как видно, порты COM1, COM3 и COM2, COM4 используют одинаковые прерывания. Поэтому, как правило, нельзя одновременно использовать порты COM1 и COM3, так же как порты COM2 и COM4. Например, если к порту COM1 подключен модем, то мышь можно подключить только к порту COM2 или COM4

    Теперь перейдем к подробному рассмотрению портов, используемых COM-портами.

    5.3.1. Регистр данных

    Регистр данных расположен непосредственно по базовому адресу COM-порта и используется для обмена данными и для задания скорости обмена.

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

    В зависимости от состояния старшего бита управляющего регистра (расположенного по адресу base_adr + 3, где base_adr соответствует базовому адресу COM-порта) назначение этого регистра может изменяться. Если старший бит равен нулю, регистр используется для записи передаваемых данных. Если же старший бит равен единице, регистр используется для ввода значения младшего байта делителя частоты тактового генератора. Изменяя содержимое делителя, можно изменять скорость передачи данных. Старший байт делителя записывается в регистр управления прерываниями по адресу base_adr + 1.

    Зависимость скорости передачи данных от значения делителя частоты представлена в следующей таблице:

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

    5.3.2. Регистр управления прерываниями

    Этот регистр используется либо для управления прерываниями от асинхронного адаптера, либо (после вывода в управляющий регистр байта с установленным в 1 старшим битом) для вывода значения старшего байта делителя частоты тактового генератора.

    В режиме управления прерываниями регистр имеет следующий формат:

    D0 Разрешение прерывания при готовности принимаемых данных. Если бит содержит единицу, генерация прерывания при готовности принимаемых данных разрешена

    D1 Разрешение прерывания после передачи байта (когда выходной буфер передачи пуст). Если бит содержит единицу, генерация прерывания после передачи байта разрешена

    D2 Разрешение прерывания по обнаружению состояния BREAK или по ошибке. Если бит содержит единицу, то при обнаружении состояния BREAK или при возникновении ошибки происходит прерывание

    D3 Разрешение прерывания по изменению состояния входных линий на разъеме RS-232-C (CTS, DSR, RI, DCD). Если бит содержит единицу, то при изменении состояния линий CTS, DSR, RI, DCD COM-порт вырабатывает прерывание

    D7-D4 Не используются, должны быть равны 0

    Для удобства доступа к регистрам UART мы определили для каждого регистра соответствующее объединение (см. файл UART_REG.H).

    Ниже мы приводим объединение, которое можно использовать для доступа к отдельным полям регистра управления прерываниями из программ на языке Си:

    // Смещение относительно базового адреса #define ICR_N 1 // Регистр управления прерываниями typedef union _ICR_ { struct { unsigned char in_ready : 1; unsigned char out_ready : 1; unsigned char err : 1; unsigned char change : 1; unsigned char reserv : 4; } bit_reg; unsigned char byte; } ICR;

    5.3.3. Регистр идентификации прерывания

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

    Формат регистра представлен ниже:

    D0 Если бит равен единице, значит нет прерываний, ожидающих обслуживания

    D2-D1 Содержит идентификатор прерывания

    В файле UART_REG.H регистр идентификации прерывания определен следующим образом:

    // Смещение относительно базового адреса #define IIDR_N 
    2 // Регистр идентификации прерывания typedef union _IIDR_ { struct { unsigned 
    char no_inter : 1; unsigned char inter_id : 2; unsigned char reserv : 5; } 
    bit_reg; unsigned char byte; } IIDR;

    5.3.4. Управляющий регистр

    Управляющий регистр доступен для записи и чтения. Этот регистр управляет различными характеристиками UART: скоростью передачи данных, контролем четности, передачей сигнала BREAK, длиной передаваемых слов (символов).

    D1-D0 Данные биты определяют длину передаваемых слов в битах:

    D2 Бит определяет количество стоповых бит:

    D3-D4 Биты управляют проверкой на четность:

    D5 Фиксация четности. При установке этого бита бит четности всегда принимает значение 0, если биты D3 D4 равны 11, или 1, если биты D3 D4 равны 01

    D6 Установка перерыва. Вызывает вывод строки нулей в качестве сигнала BREAK для подключенного устройства

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

    Для облегчения доступа к отдельным полям управляющего регистра можно воспользоваться следующим объединением:

    // Смещение относительно базового адреса #define LCR_N 3 // Управляющий регистр typedef union _LCR_ { struct { unsigned char len : 2; unsigned char stop : 1; unsigned char parity : 2; unsigned char stuck_parity : 1; unsigned char en_break_ctl : 1; unsigned char dlab : 1; } bit_reg; unsigned char byte; } LCR;

    5.3.5. Регистр управления модемом

    Регистр управления модемом управляет состоянием выходных линий DTR, RTS и линий, специфических для модемов - OUT1 и OUT2. Формат регистра представлен ниже:

    D0 Линия DTR. Сигнал подтверждения связи. Используется модемами для разрешения передачи данных между компьютером и микросхемой UART

    D1 Линия RTS. Сигнал подтверждения связи. Используется модемами для разрешения передачи данных между компьютером и микросхемой UART

    D2 Линия OUT1. Для некоторых модемов при установке этого бита в единицу происходит его аппаратный сброс

    D3 Линия OUT2. Если бит D3 содержит единицу, то UART может вырабатывать прерывания, а если нулю - не может

    D4 Запуск диагностики при входе асинхронного адаптера, замкнутом на его выход (Digital Loopback test). Эта возможность реализована только для асинхронных портов, использующих микросхему UART 8250, или полностью совместимых с ней

    D7-D5 Должны быть равны 0

    Регистр управления модемом определен нами в файле UART_REG.H следующим образом:

    // Смещение относительно базового адреса #define MCR_N 4 // Регистр управления модемом typedef union _MCR_ { struct { unsigned char dtr : 1; unsigned char rts : 1; unsigned char out1 : 1; unsigned char out2 : 1; unsigned char diag : 1; unsigned char reserv : 3; } bit_reg; unsigned char byte; } MCR;

    5.3.6. Регистр состояния линии

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

    D0 Данные получены и готовы для чтения, при чтении данных бит сбрасывается

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

    D2 Ошибка четности, сбрасывается после чтения состояния линии

    D3 Ошибка синхронизации. Возникает, например, при отсутствии стоп-битов в принятом байте

    D4 Обнаружен запрос на прерывание передачи BREAK - длинная строка нулей

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

    D6 Регистр сдвига передатчика пуст. Этот регистр получает данные из регистра хранения и преобразует их в последовательный вид для передачи. Если этот бит равен единице, то UART может принять очередной символ от компьютера

    D7 Тайм-аут (устройство не связано с компьютером)

    Доступ к отдельным полям регистра состояния линии можно организовать с помощью следующего объединения:

    // Смещение относительно базового адреса #define LSR_N 5 
    // Регистр состояния линии typedef union _LSR_ { struct { unsigned char in_ready 
    : 1; unsigned char overflow : 1; unsigned char parity : 1; unsigned char synxr : 
    1; unsigned char break_detect : 1; unsigned char out_ready : 1; unsigned char 
    shift_ready : 1; unsigned char taimout : 1; } bit_reg; unsigned char byte; } 
    LSR;

    5.3.7. Регистр состояния модема

    Регистр состояния модема позволяет программе определить состояние управляющих сигналов, передаваемых модемом асинхронному порту компьютера. Формат регистра состояния модема представлен ниже:

    D0 Если бит D0 равен единице, линия CTS изменила состояние

    D1 Если бит равен единице, линия DSR изменила состояние

    D2 Если бит D2 равен единице, линия RI изменила состояние. Некоторые коммуникационные программы определяют по состоянию этого бита наличие звонка на телефонной линии

    D3 Если данный бит равен единице, значит линия DCD изменила свое состояние. Некоторые коммуникационные программы определяют по состоянию этого бита, установил ли модем соединение с удаленным модемом

    D4 Бит соответствует состоянию линии CTS. Эта линия используется совместно с линией RTS при реализации аппаратного управления потоком данных

    D5 Бит соответствует состоянию линии DSR. Эта линия используется совместно с линией DTR при аппаратной реализации подтверждения связи

    D6 Бит соответствует состоянию линии RI. Единица означает, что модем обнаружил звонок на телефонной линии

    D7 Состояние линии DCD. Единица означает, что модемом получена несущая частота. Заметим, что при выполнении аналогового теста (analog test) этот бит должен содержать единицу. Если это не так, то возможно, что модем исправен (для внешних модемов), но кабель, соединяющий модем и компьютер, не полностью соответствует стандарту RS-232

    Доступ к отдельным полям регистра состояния модема можно организовать с помощью следующего объединения:

    // Смещение относительно базового адреса #define MSR_N 6 
    // Регистр состояния модема typedef union _MSR_ { struct { unsigned char 
    change_cts : 1; unsigned char change_dsr : 1; unsigned char change_ri : 1; 
    unsigned char change_dcd : 1; unsigned char cts : 1; unsigned char dsr : 1; 
    unsigned char ri : 1; unsigned char dcd : 1; } bit_reg; unsigned char byte; } 
    MSR;

    5.4. Современные микросхемы UART

    Микросхема UART 8250 в ее исходном виде использовалась только в старых моделях компьютеров IBM PC и IBM XT. Современные микросхемы - UART 16450, 16550 и 16550A, изготовленные по новой технологии, позволяют достичь более высокой скорости обмена данными, а также обладают новыми аппаратными возможностями. В этой главе мы рассмотрим основные различия между 8250 и новыми микросхемами, а также приведем дополнительную информацию по программированию UART 16550A.

    Опишем основные возможности различных микросхем UART:

  • 8250 (иногда называют 8250-B): Использовалась на первых моделях компьютеров IBM PC и IBM XT
  • 16450 (иногда обозначают как 8250-A): Эта микросхема используется в основном для IBM PC/AT, так как имеет большую производительность. Фактически это микросхема UART 8250, но изготовленная с использованием новой технологии. Микросхема UART 16450 дополнена регистром расширения (scratch register), имеющим адрес 3FFh (base_adr + 7). В ней также устранены ошибки в регистре разрешения прерываний и добавлена возможность перевода линии OUT2 во время проведения тестов в высокоимпедансное состояние
  • 16550: Практически полностью соответствует UART 16450. Добавлена возможность внутренней буферизации передаваемых и принимаемых данных. Буферы выполнены по схеме FIFO (First In First Out - первый вошел, первым вышел) или, другими словами, в виде очереди. При использовании буферизации можно заметно уменьшить число прерываний, вырабатываемых асинхронным портом. Однако из-за ошибки в микросхеме эту возможность лучше не использовать - можно потерять отдельные символы. В общем случае микросхема 16550 более быстрая, чем 16450. Дополнительно 16550 дает возможность использовать несколько каналов прямого доступа (DMA channels)
  • 16550A (иногда обозначают как 16550AN) Соответствует 16550, но в ней исправлены ошибки реализации буфера FIFO. Эта микросхема дает возможность использования нескольких каналов прямого доступа. 16550A, как правило, используется в компьютерах с процессорами 80386/486 и в компьютерах с RISC-архитектурой. Заметим, что, если вы хотите работать на скоростях больших, чем 9600 бит/с, вам желательно использовать именно эту микросхему.

    5.4.1. Как определить тип микросхемы UART

    Как же определить, какая из этих микросхем UART установлена на вашем асинхронном адаптере? Кроме возможности заглянуть в документацию, существует еще один способ. Фактически этот способ основан на различиях в особенностях микросхем UART. Ниже приведены особенности микросхем UART различных типов:

  • UART 8250 не имеет регистра расширения
  • UART 16450 не имеет внутренних буферов FIFO
  • UART 16550 имеет внутренние буфера FIFO, но с ошибками. Бит D7 регистра управления прерываниями (регистр IIR) равен единице, а бит D6 - нулю
  • UART 16550A не содержит ошибок при реализации FIFO. Биты D7 и D6 регистра управления прерываниями (регистр IIR) равны единице.

    Согласно этим особенностям микросхем UART возможен следующий алгоритм определения их типа:

  • Читаем и сохраняем значение регистра расширения. Адрес регистра расширения определяем как базовый адрес плюс семь (base_adr + 7)
  • Записываем в регистр расширения какое-либо число, например, 0A5h
  • Снова считываем значение регистра расширения и сравниваем его с числом 0A5h, записанным в него ранее. Если эти значения не равнозначны, значит регистр расширения отсутствует и, следовательно, тип проверяемой микросхемы UART - 8250
  • Запоминаем в регистре расширения другое число, например, 5Ah
  • Снова считываем значение регистра расширения и сравниваем его с ранее записанным числом. Если эти значения не одинаковы, значит регистр расширения отсутствует и, следовательно, тестируемая микросхема UART 8250
  • Восстанавливаем величину, изначально хранившуюся в регистре расширения
  • Считываем и сохраняем значение регистра управления прерываниями
  • Записываем единицу в регистр управления режимом буферизации (регистр FCR, подробно описан ниже)
  • Считываем значение регистра управления прерываниями. Если бит D7 сохраненного регистра управления прерываниями содержит нулевое значение, запоминаем единицу в регистре FCR
  • Если бит D6 регистра управления прерываниями содержит единицу, тип микросхемы UART - 16550A
  • Если бит D7 регистра управления прерываниями содержит единицу, тестируемая микросхема - UART 16550
  • В противном случае мы имеем микросхему UART 16450.

    5.4.2. Программа для определения типа микросхемы UART

    Теперь приведем программу TST_UART, реализующую изложенный алгоритм. В данной программе используются созданные нами функции is_UART_8250() и is_UART_FIFO(). Первая позволяет определить по отсутствию регистра расширения микросхему UART 8250, а вторая по особенностям реализации внутреннего буфера данных различает остальные типы микросхем.

    Исходный текст программы TST_UART представлен в листинге 5.1.

    Листинг 5.1. Файл TST_UART.C

    
    // Программа определения типа микросхемы UART асинхронного 
    // последовательного адаптера
    
    #define    UART_8250     1
    #define    UART_16450    2
    #define    UART_16550    3
    #define    UART_16550A   4
    
    void main(void) {
    
    	// Номер асинхронного порта может быть 0 для COM1
    	// или 1 для COM2
    
    	int port = 0;
    	int test;
    
    	printf("\n(c) Frolov G.V. 1992-1994.   "
    		   "Программа определения типа UART\n\n");
    
    	printf(	"\Введите номер асинхронного"
    					"порта (COM1 - 0, COM2 - 1):");
    
    	scanf( "%d", &port );
    	if(( port != 0 ) && ( port != 1 )){
    		printf( "асинхронный порт COM%d не поддерживается\n",
    						port );
    		exit( -1 );
    	}
    
    	// Проверяем, является ли микросхема UART - UART 8250
    	if( is_UART_8250(port) == UART_8250 ) {
    		printf("Обнаружена микросхема UART 8250\n");
    		exit(0);
    	}
    
    	// Проверяем другие типы микросхем UART
    	if(( test = is_UART_FIFO(port) ) == UART_16550A ) {
    		printf("Обнаружена микросхема UART 16550A\n");
    		exit(0);
    	}
    
    	else if(test == UART_16550) {
    		printf("Обнаружена микросхема UART 16550\n");
    		exit(0);
    	}
    
    	printf("Обнаружена микросхема UART 16450\n");
    }
    
    /**
    *.Name         is_UART_8250
    *
    *.Descr        Функция определяет тип микросхемы,
    *              используемый данным последовательным асинхронным
    *              адаптером (UART).
    *
    *.Proto        int is_UART_8250( int port );
    *
    *.Params       int port - номер асинхронного адаптера:
    *                 0 - COM1, 1 - COM2
    *
    *.Return       Для UART 8250 - возвращает константу UART_8250,
    *              в остальных случаях возвращает 0
    **/
    int is_UART_8250( int port ) {
    
    	int save_scr, in_scr;
    
    	// Сохраняем значения регистра расширения
    	save_scr = inp( 0x3ff - 0x100 * port );
    
    	// Записываем в регистр расширения число 0x5A
    	outp( 0x3ff - 0x100 * port, 0x5A );
    
    	// Считываем регистр расширения
    	in_scr = inp( 0x3ff - 0x100 * port  );
    
    	// Сохранилось ли записанное число?
    	if( in_scr != 0x5A ) {
    
    		// Если нет, значит, регистр расширения отсутствует и,
    		// следовательно, тип микросхемы - UART 8250
    
    		// Восстанавливаем значение регистра расширения
    		outp( 0x3ff - 0x100 * port, save_scr );
    		return( UART_8250 );
    	}
    
    	// Записываем в регистр расширения другое число - 0xA5
    	outp( 0x3ff - 0x100 * port, 0xA5 );
    
    	// Считываем регистр расширения
    	in_scr = inp( 0x3ff - 0x100 * port  );
    
    	// Восстанавливаем значение регистра расширения
    	outp( 0x3ff - 0x100 * port, save_scr );
    
    	// Сохранилось ли записанное число?
    	if( in_scr != 0xA5 )
    
    		// Если нет, регистр расширения отсутствует и,
    		// следовательно, тип микросхемы - UART 8250
    		return( UART_8250 );
    
    	// В противном случае регистр расширения есть и надо 
    	// выполнить дальнейшее тестирование для определения 
    	// типа UART
    	return( 0 );
    }
    
    /**
    *.Name         is_UART_FIFO
    *
    *.Descr        Функция определяет тип микросхемы,
    *              используемой данным последовательным асинхронным
    *              адаптером (UART).
    *
    *.Proto        int is_UART_FIFO( int port );
    *
    *.Params       int port - номер асинхронного адаптера:
    *                 0 - COM1, 1 - COM2
    *
    *.Return       для UART 164550 возвращает константу UART_16450,
    *              для UART 16550  возвращает константу UART_16550,
    *              для UART 16550A возвращает константу UART_16550A
    **/
    int is_UART_FIFO( int port ) {
    
    	int save_iir, in_iir;
    
    	// Сохраняем значение регистра определения прерывания
    	save_iir = inp( 0x3fa - 0x100 * port );
    
    	// Разрешаем использование FIFO
    	outp( 0x3fa - 0x100 * port, 0x1 );
    
    	// Читаем значение регистра определения прерывания
    	in_iir = inp( 0x3fa - 0x100 * port  );
    
    	// Восстанавливаем  значение регистра определения прерывания
    	outp( 0x3fa - 0x100 * port, 0x0 );
    
    	// Если бит D6 содержит единицу, значит, мы имеем UART 16550A
    	if(( in_iir & 0x40 ) == 1 )
    		return( UART_16550A );
    
    	// Если бит D7 содержит единицу, значит, мы имеем UART 16550
    	if(( in_iir & 0x80 ) == 1 )
    		return( UART_16550 );
    
    	// Если биты D7 и D6 содержат нули, значит, мы имеем UART 
    	// 16450 (буфер FIFO отсутствует)
    	return( UART_16450 );
    }
    

    5.4.3. Изменения в регистрах UART 16550A

    В этой главе мы рассмотрим изменения в формате регистров UART 16550A по сравнению с UART 8250.

    Начнем с регистра идентификации прерывания. Этот регистр доступен только для чтения. По сравнению с UART 8250 в нем добавлены два бита - D6 и D7, которые показывают состояние буфера FIFO.

    Если биты D7 и D6 оба равны единице, то разрешено использование буферизации (FIFO). Если же только бит D7 содержит единицу, это означает, что вы имеете дело с микросхемой UART 16550. В ней режим буферизации реализован с ошибками, и использовать его не надо.

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

    Если бит D3 содержит единицу, то бит D2 также содержит единицу. Это означает, что буфер приемника содержит данные.

    Для микросхем UART 8250 и 16450 биты D3, D6 и D7 всегда содержат нули. Биты D4 и D5 не используются во всех рассматриваемых микросхемах.

    Для управления режимом буферизации UART 16550A имеет дополнительный регистр - регистр управления буферизацией FIFO. Этот регистр разделяет общий адрес с регистром идентификации прерываний - base_adr + 2. Но в отличие от регистра идентификации прерываний, доступного только для чтения, этот регистр доступен только для записи.

    Итак, регистр управления режимом буферизации имеет следующий формат:

    D0 Установка этого бита в единицу разрешает использование буферизации для принимаемых и передаваемых данных. Этот бит должен содержать единицу, если какой-либо из других битов содержит единицу

    D1 Сброс приемного буфера. При установке этого бита в единицу буфер приемника очищается. Затем бит автоматически сбрасывается в ноль

    D2 Сброс буфера передатчика. При установке этого бита в единицу буфер передатчика очищается. Затем бит автоматически сбрасывается в ноль

    D3 Выбор режима прямого доступа. Бит не используется на большинстве последовательных асинхронных адаптеров

    D4-D5 Не используются

    D7, D6 Управление прерываниями от приемника. Если буферизация отсутствует, то прерывание происходит всякий раз при приеме нового символа. С разрешенной буферизацией UART может генерировать прерывание при получении заданного количества символов:

    Биты D7 D6

    Количество символов, байт

    00

    1

    01

    4

    10

    8

    11

    14

    5.4.4. Как использовать буферизацию?

    Обычно без использования буферизации UART генерирует прерывание всякий раз, когда передается или принимается очередной символ. В результате при скорости 2400 бит/с прерывания происходят с частотой 240 прерываний за одну секунду. Это не очень много, но при увеличении скорости до максимально возможной - 115200 бит/с за секунду - происходит уже 11520 прерываний. 11520 прерываний за одну секунду - это уже много. Использование буферизации позволяет при той же скорости резко сократить количество прерываний. Так, при генерации прерываний каждые 14 символов (бит регистра управления буферизацией D7 = 1, D6 = 1) за секунду произойдет около 823 прерываний.

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

    При программировании UART 16550A для использования режима буферизации необходимо выполнить следующие действия:

  • Когда для определения причины прерывания считывается регистр идентификации прерывания, надо использовать только три младших бита. Для этого можно замаскировать полученное значение числом 07h
  • После обычной инициализации UART надо разрешить использование буферизации, записав в регистр управления буферизацией число 0C7h. При этом будет разрешено использование буферизации, выполнена очистка буферов приемника и передатчика, а также вызвана генерация прерываний при записи в буфер приемника больше 14 символов. После этого следует прочитать содержимое регистра идентификации прерывания (по тому же адресу). Если бит D6 этого регистра не установлен, то ваша микросхема UART не является 16550A. И вам следует запретить использование буферизации, записав ноль в регистр управления буферизацией.

    Операционная система Windows 3.1 поддерживает новые возможности асинхронных последовательных адаптеров, созданных на основе микросхемы UART 16550A. Поэтому, когда вы пишете приложение для Windows, нет необходимости самостоятельно определять тип микросхемы UART.

    5.5. Первая телекоммуникационная программа

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

    Программа работает с асинхронным адаптером COM1. Для правильной работы необходимо замкнуть вместе контакты 2 и 3 разъема COM1. В этом случае данные передаваемые в COM-порт сразу же принимаются им обратно.

    Главный файл программы AUX_TEST представлен в листинге 5.2.

    Листинг 5.2. Файл AUX_TEST.C

    #include 
    #include 
    #include "sysp_com.h"
    
    void main(void);
    void main(void) {
    
    	AUX_MODE amd;
    
    	aux_stat(&amd, 0);
    	printf("\nСостояние порта COM1:"
    			 "\nКод длины символа:    %d"
    			 "\nКод числа стоп-битов: %d"
    			 "\nКонтроль четности:    %d"
    			 "\nСкорость передачи:    %lu",
    			 amd.ctl_aux.ctl_word.len,
    			 amd.ctl_aux.ctl_word.stop,
    			 amd.ctl_aux.ctl_word.parity,
    			 (unsigned long)amd.baud);
    
    	amd.baud = 115200;
    
    	aux_init(&amd, 0, 0);
    
    	printf("\n\nТестирование асинхронного адаптера."
    			 "\nНажимайте клавиши!"
    			 "\nДля завершения работы нажмите CTRL-C"
    			 "\n");
    
    	for(;;) {
    
    		// Вводим символ с клавиатуры и передаем его
    		// в асинхронный адаптер
    		aux_outp(getch(), 0);
    
    		// Вводим символ из асинхронного адаптера и
    		// отображаем его на экране
    
    		putchar(aux_inp(0));
    	}
    }
    

    После запуска программы AUX_TEST вызывается функция aux_stat, определенная в нашем приложении. Она заполняет структуру amd типа AUX_MODE, записывая в нее информацию о текущем режиме COM-порта.

    Информация, записанная в этой структуре, отображается на экране. Изменяем поле baud структуры amd, содержащее значение скорости передачи данных, записывая в нее число 115200. Затем функция aux_init, определенная в программе, увеличивает скорость передачи информации до 115200 бит/с.

    Затем начинает выполняться бесконечный цикл, в котором вводится символ с клавиатуры и передается в COM-порт:

    aux_outp(getch(), 0);

    Сразу после передачи кода нажатой клавиши в COM-порт, вводим символ из этого же порта и отображаем его на экране:

    putchar(aux_inp(0));

    Чтобы завершить работу программы, достаточно нажать комбинацию клавиш .

    Включаемый файл SYSP.H, представленный в листинге 5.3, содержит описания функций aux_stat, aux_init, aux_inp, aux_outp и определение структуры AUX_MODE.

    Листинг 5.3. Файл SYSP.H

    void aux_stat(AUX_MODE *mode, int port);
    int  aux_init(AUX_MODE *mode, int port, int imask);
    void aux_outp(char chr, int port);
    char aux_inp(int port);
    
    typedef struct _AUX_MODE_ {
    
     union {
       struct {
          unsigned char len : 2, // длина символа
               stop         : 1, // число стоп-битов
               parity       : 2, // контроль четности
               stuck_parity : 1, // фиксация четности
               en_break_ctl : 1, // установка перерыва
               dlab         : 1; // загрузка регистра 
    												   // делителя
       } ctl_word;
       char ctl;
     } ctl_aux;
    
     unsigned long baud; // скорость передачи данных
    
    } AUX_MODE;
    

    Теперь перейдем к рассмотрению функций aux_stat, aux_init, aux_inp и aux_outp, определенных в программе AUX_TEST.

    5.5.1. Инициализация асинхронного адаптера

    Первое, что должна сделать программа, работающая с асинхронным адаптером, - установить формат и скорость передачи данных. После загрузки операционной системы для асинхронных адаптеров устанавливается скорость 2400 бит/с, проверка на четность не выполняется, посылаются восемь бит данных и один стоповый бит. Вы можете изменить этот режим командой MS-DOS MODE. Описание этой команды представлено в приложении "Команда MODE операционной системы MS-DOS".

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

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

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

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

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

    Листинг 5.4. Файл AUX_STAT.C

    /**
    *.Name         aux_stat
    *
    *.Descr        Функция считывает текущий режим
    *              асинхронного порта и записывает его
    *              в структуру с типом AUX_MODE.
    *
    *.Proto        void aux_stat(AUX_MODE *mode, int port);
    *
    *.Params       AUX_MODE mode - структура, описывающая
    *              протокол и режим работы порта
    *
    *              int port - номер асинхронного адаптера:
    *                 0 - COM1, 1 - COM2
    *
    *.Return       Ничего
    **/
    
    #include 
    #include 
    #include "sysp_com.h"
    
    void aux_stat(AUX_MODE *mode, int port) {
    
    	unsigned long b;
    
    	// Запоминаем режим адаптера
    	mode->ctl_aux.ctl = (char)inp(0x3fb - 0x100 * port);
    
    	// Устанавливаем старший бит режима
    	// для считывания текущей скорости передачи
    	outp(0x3fb - 0x100 * port, mode->ctl_aux.ctl | 0x80);
    
    	// Считываем значение регистра делителя
    	b = inp(0x3f9 - 0x100 * port); b = b << 8;
    	b += inp(0x3f8 - 0x100 * port);
    
    	// Преобразуем его в боды
    
    	switch (b) {
    		case 1040: b = 110; break;
    		case 768: b = 150; break;
    		case 384: b = 300; break;
    		case 192: b = 600; break;
    		case 96: b = 1200; break;
    		case 48: b = 2400; break;
    		case 24: b = 4800; break;
    		case 12: b = 9600; break;
    		case 6: b = 19200; break;
    		case 3: b = 38400; break;
    		case 2: b = 57600; break;
    		case 1: b = 115200; break;
    		default: b=0; break;
    	}
    
    	mode->baud = b;
    
    	// Восстанавливаем состояние адаптера
    	outp(0x3fb - 0x100 * port, mode->ctl_aux.ctl & 0x7f);
    }
    
    

    Прочитав состояние адаптера, вы можете изменить нужные вам поля в структуре AUX_MODE и вызвать функцию aux_init для изменения текущего режима COM-порта. Исходный текст этой функции можно найти в листинге 5.5.

    Листинг 5.5. Файл AUX_INIT.C

    /**
    *.Name         aux_init
    *
    *.Descr        Функция инициализирует асинхронные
    *              адаптеры, задавая протокол обмена данными
    *              и скорость обмена данными.
    *
    *.Proto        int aux_init(AUX_MODE *mode, int port,
    *							int imask);
    *
    *.Params       AUX_MODE *mode - указатель на структуру,
    *							описывающую протокол и режим работы 
    *							порта;
    *
    *              int port - номер асинхронного адаптера:
    *                 0 - COM1, 1 - COM2
    *
    *              int imask - значение для регистра маски
    *                          прерываний
    *
    *.Return       0 - инициализация выполнена успешно;
    *              1 - ошибки в параметрах инициализации.
    *
    *.Sample       aux_test.c
    **/
    
    #include 
    #include 
    #include "sysp_com.h"
    
    int aux_init(AUX_MODE *mode, int port, int imask) {
    
    	unsigned div;
    	char ctl;
    
    	// Вычисляем значение для делителя
    	switch (mode->baud) {
    		case 110: div = 1040; break;
    		case 150: div = 768; break;
    		case 300: div = 384; break;
    		case 600: div = 192; break;
    		case 1200: div = 96; break;
    		case 2400: div = 48; break;
    		case 4800: div = 24; break;
    		case 9600: div = 12; break;
    		case 19200: div = 6; break;
    		case 38400: div = 3; break;
    		case 57600: div = 2; break;
    		case 115200: div =1; break;
    		default: return(-1); break;
    	}
    
    	// Записываем значение делителя частоты
    	ctl = inp(0x3fb - 0x100 * port);
    	outp(0x3fb - 0x100 * port, ctl | 0x80);
    
    	outp(0x3f9 - 0x100 * port, (div >> 8) & 0x00ff);
    	outp(0x3f8 - 0x100 * port, div & 0x00ff);
    
    	// Записываем новое управляющее слово
    	outp(0x3fb - 0x100 * port, mode->ctl_aux.ctl & 0x7f);
    
    	// Устанавливаем регистр управления прерыванием
    	outp(0x3f9 - 0x100 * port, imask);
    
    	return(0);
    }
    

    5.5.2. Передача данных

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

    Признаком того, что регистр передатчика свободен, является установленный в 1 бит 5 регистра состояния линии с адресом base_adr + 5. Функция aux_outp (см. листинг 5.6) ждет окончания передачи текущего символа, затем посылает в асинхронный адаптер следующий символ.

    Листинг 5.6. Файл AUX_OUTP.C

    /**
    *.Name         aux_outp
    *
    *.Descr        Функция дожидается готовности
    *              передатчика и посылает символ.
    *
    *.Proto        void aux_outp(char chr, int port);
    *
    *.Params       char chr - посылаемый символ;
    *
    *              int port - номер асинхронного адаптера:
    *                 0 - COM1, 1 - COM2
    *
    *.Return       Ничего
    ***/
    
    #include 
    #include 
    #include "sysp_com.h"
    
    void aux_outp(char chr, int port) {
    
    	unsigned status_reg, out_reg;
    
    	status_reg = 0x3fd - 0x100 * port;
    	out_reg = status_reg - 5;
    
    	while( (inp(status_reg) & 0x20) == 0 );
    
    	outp(out_reg, chr);
    }
    

    Аналогично передаче данных перед вводом символа из регистра данных (адрес base_adr) необходимо убедиться в том, что бит 0 регистра состояния линии (адрес base_adr + 5) установлен в 1. Это означает, что символ принят из линии и находится в буферном регистре приемника.

    Для приема данных мы используем функцию aux_inp (см. листинг. 5.7).

    Листинг 5.7. Файл AUX_INP.C

    /**
    *.Name         aux_inp
    *
    *.Descr        Функция дожидается готовности
    *              приемника и вводит символ из асинхронного
    *              адаптера.
    *
    *.Proto        char aux_inp(int port);
    *
    *.Params       int port - номер асинхронного адаптера:
    *                 0 - COM1, 1 - COM2
    *
    *.Return       Принятый символ
    *
    *.Sample       aux_test.c
    **/
    
    #include 
    #include 
    #include "sysp_com.h"
    
    char aux_inp(int port) {
    
    	unsigned status_reg, inp_reg;
    
    	status_reg = 0x3fd - 0x100 * port;
    	inp_reg = status_reg - 5;
    
    	while( (inp(status_reg) & 1) == 0 );
    
    	return(inp(inp_reg));
    }
    

    5.6. Обработка прерываний COM-порта

    Так как процесс последовательной передачи данных протекает достаточно медленно, имеет смысл выполнять его в фоновом режиме, используя прерывания по окончании передачи или приема символа. Напомним, что, как правило, портам COM1 и COM3 соответствует аппаратное прерывание INT 0Ch, а COM2 и COM4 - INT 0Bh.

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

    Когда произошло прерывание, программа-обработчик прерывания должна проанализировать причину прерывания, прочитав содержимое регистра идентификации прерывания с адресом base_adr + 2.

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

    Так следует поступать до тех пор, пока бит 0 регистра идентификации прерывания не станет равным нулю.

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

    В предыдущих томах из серии "Библиотека системного программиста" мы рассказывали о контроллере прерываний и о механизме прерываний в персональном компьютере IBM PC/XT/AT. Без умения работать с контроллером прерываний вы не сможете использовать режим прерываний при программировании последовательного асинхронного порта.

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

    5.6.1. Механизм прерываний

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

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

    Использование прерываний при работе с медленными внешними устройствами позволяет совместить ввод/вывод с обработкой данных в центральном процессоре и в результате повышает общую производительность системы.

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

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

    5.6.2. Таблица векторов прерываний

    Для того чтобы связать адрес обработчика прерывания с номером прерывания, используется таблица векторов прерываний, занимающая первый килобайт оперативной памяти - адреса от 0000:0000 до 0000:03FF. Таблица состоит из 256 элементов - адресов обработчиков прерываний, состоящих из компонент сегмента и смещения. Эти элементы называются векторами прерываний. В первом слове элемента таблицы записано смещение, а во втором - адрес сегмента обработчика прерывания.

    Прерыванию с номером 0 соответствует адрес 0000:0000, прерыванию с номером 1 - 0000:0004 и т. д.

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

    5.6.3. Маскирование прерываний

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

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

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

    5.6.4. Обработка аппаратных прерываний и контроллер прерываний

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

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

    Система приоритетов реализована на двух микросхемах Intel 8259 (для машин класса XT - на одной такой микросхеме). Каждая микросхема обслуживает до восьми приоритетов. Микросхемы можно объединять (каскадировать) для увеличения количества уровней приоритетов в системе.

    Уровни приоритетов обозначаются сокращенно IRQ0 - IRQ15 (для машин класса XT существуют только уровни IRQ0 - IRQ7).

    Для машин XT приоритеты линейно зависели от номера уровня прерывания. IRQ0 соответствовало самому высокому приоритету, за ним шли IRQ1, IRQ2, IRQ3 и так далее. Уровень IRQ2 в машинах класса XT был зарезервирован для дальнейшего расширения системы. И начиная с машин класса AT IRQ2 стал использоваться для каскадирования контроллеров прерывания 8259. Добавленные приоритетные уровни IRQ8 - IRQ15 в этих машинах располагаются по приоритету между IRQ1 и IRQ3.

    Приведем таблицу аппаратных прерываний, расположенных в порядке приоритета:

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

    Для управления схемами приоритетов необходимо знать внутреннее устройство контроллера прерываний 8259. Поступающие прерывания запоминаются в регистре запроса на прерывание IRR. Каждый бит из восьми в этом регистре соответствует прерыванию. После проверки на обработку в настоящий момент другого прерывания запрашивается информация из регистра обслуживания ISR. Перед выдачей запроса на прерывание в процессор проверяется содержимое восьмибитового регистра маски прерываний IMR. Если прерывание данного уровня не замаскировано, то выдается запрос на прерывание.

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

    Контроллер 8259 имеет несколько режимов работы, которые устанавливаются программным путем. В персональных компьютерах XT и AT за первоначальную установку режимов работы микросхем 8259 отвечает BIOS. У программиста, скорее всего, не возникнет потребность перепрограммировать контроллер - это небезопасно, так как неправильное программирование контроллера приведет к нарушению логики работы всей системы.

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

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

    Если контроллеры 8259 каскадированы, то ведомой микросхеме присваивается код (выдачей в микросхему соответствующего командного слова). Этот код равен номеру входа IRQ_ведущей микросхемы, с которым соединен выход запроса прерывания INT ведомой микросхемы. Внутри микросхемы приоритет зависит от номера IRQ и задается программно. Для компьютеров XT и AT самым высоким приоритетом внутри группы, обслуживаемой каждым контроллером, является вход IRQ0. Однако возможно программное изменение приоритетов в рамках так называемого приоритетного кольца. При этом дно приоритетного кольца имеет самый низкий приоритет.

    Приведем возможные варианты задания приоритетов:

    Наиболее высокий приоритет у входа IRQ с обозначением 0 приоритетного кольца, наиболее низкий - с обозначением 7.

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

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

  • Индивидуальное маскирование запросов прерывания;
  • Специальное маскирование обслуженных запросов;
  • Установка статуса уровней приоритета (по установке исходного состояния, по обслуженному запросу, по указанию);
  • Операции конца прерывания (обычный конец прерывания, специальный конец прерывания, автоматический конец прерывания);
  • Чтение регистров IRR, ISR, IMR.

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

    Рассмотрим команды операций. Существует три типа команд операций:

    1. Маскирование запросов прерывания.

    2. Команды обработки конца прерывания.

    3. Опрос регистров и специальное маскирование.

    Байты команды маскирования запросов прерывания выводятся соответственно в порты 21h и A1h для первого и второго контроллера 8259 компьютера AT. Команды операций второго и третьего типа используют порты с адресами 20h и A0h.

    Для маскирования какого-либо уровня прерывания надо записать в регистр маски IMR по адресу 21h или A1h единицу в разряд регистра, соответствующий этому уровню. Например, для маскирования прерываний от НГМД в порт 21h надо заслать двоичное число 01000000.

    В листинге 5.8 приведен пример программы LOCKFDD, маскирующей прерывание от флоппи-диска.

    Листинг 5.8. Файл LOCKFDD.C

    #include 
    #include 
    #include 
    
    void main(void);
    
    void main(void) {
    
     	outp(0x21,0x40);
    	printf("\nПрерывания от флоппи-диска запрещены.\n");
    
    	exit(0);
    }

    Чтобы "оживить" флоппи-диски, запустите программу UNLOCK, которая размаскирует все прерывания (в том числе и от флоппи-диска). Исходный текст этой программы представлен в листинге 5.9.

    Листинг 5.9. Файл UNLOCK.C

    #include 
    #include 
    #include 
    
    void main(void);
    
    void main(void) {
    
    	outp(0x21,0);
    	printf("\nПрерывания от флоппи-диска разрешены.\n");
    
    	exit(0);
    }
    

    Заметьте, что мы только что замаскировали прерывание именно от флоппи-диска, все остальные устройства продолжали нормально работать. Если бы мы выдали машинную команду CLI, то отключились бы все аппаратные прерывания. Это привело бы, например, к тому, что клавиатура была бы заблокирована.

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

    Команды обработки конца прерывания приведем в виде таблицы:

    Команды третьего типа выдаются также в порты с адресами 20h и A0h. Они имеют следующий формат:

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

    Команда специального конца прерывания устанавливает в нулевое состояние тот разряд ISR, номер которого указан в разрядах B0...B2 команды.

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

    Аналогично работает команда циклического сдвига уровней приоритета со специальным концом прерывания, только низший уровень приоритета присваивается тому входу IRQ, номер которого указан в разрядах B0...B2 команды.

    Команда циклического сдвига уровней приоритета устанавливает статус уровней приоритета без выполнения операции конца прерывания. Разряды B0...B2 указывают дно приоритетного кольца.

    После выполнения команд разрешения чтения регистров ISR или IRR при выполнении команды ввода из порта 20h и A0h считывается соответственно содержимое регистров ISR и IRR. Для получения содержимого регистра IMR необходимо выполнить чтение портов с адресами соответственно 21h и A1h.

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

    Чтение регистров ISR и IRR может использоваться резидентными программами при проверке возможности своей активизации - можно проверить, не выполняется ли в настоящий момент обработка какого-нибудь прерывания, которая может конфликтовать с действиями резидентной программы.

    5.7. Поддержка асинхронного адаптера в BIOS

    В этом разделе мы опишем функции BIOS, облегчающие обслуживание асинхронного последовательного адаптера. Эти функции доступны через прерывание INT 14h.

    Первая функция предназначена для инициализации портов асинхронного адаптера:

    На входе: AH = 00h;

    DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3, 3 - COM4;

    AL = параметры инициализации (см. ниже).

    На выходе: AH = состояние порта асинхронного адаптера;

    AL = состояние модема.

    При вызове этой функции регистр AL должен содержать параметры инициализации (x - состояние бита безразлично):

    Биты

    Смысл

    D1 D0

    Длина слова в битах:</p>

    <p>00 - 5 бит;</p>

    <p>01 - 6 бит;</p>

    <p>10 - 7 бит;</p>

    <p>11 - 8 бит

    D2

    Количество стоповых бит:</p>

    <p>0 - 1 бит;</p>

    <p>1 - 2 бита

    D4 D3

    Четность:</p>

    <p>x0 - контроль на четность не используется;</p>

    <p>01 - контроль на нечетность;</p>

    <p>11 - контроль на четность

    D7-D5

    Скорость передачи данных:</p>

    <p>000 - 110 бит/с</p>

    <p>001 - 150 бит/с</p>

    <p>010 - 300 бит/с</p>

    <p>011 - 600 бит/с</p>

    <p>100 - 1200 бит/с</p>

    <p>101 - 2400 бит/с</p>

    <p>110 - 4800 бит/с</p>

    <p>111 - 9600 бит/с

    После вызова функции в регистр AH записывается состояние порта асинхронного адаптера:

    Бит

    Смысл

    D0

    Тайм-аут, если установлен этот бит, другие биты не имеют значения

    D1

    Регистр сдвига передатчика пуст

    D2

    Буферный регистр передатчика пуст

    D3

    Обнаружено состояние BREAK

    D4

    Ошибка синхронизации

    D5

    Ошибка четности

    D6

    Ошибка переполнения входного регистра

    D7

    Данные готовы

    Регистр AL содержит байт состояния модема:

    Бит

    Смысл

    D0

    Линия CTS изменила состояние

    D1

    Линия DSR изменила состояние

    D2

    Линия RI изменила состояние

    D3

    Линия DCD изменила состояние

    D4

    Состояние линии CTS

    D5

    Состояние линии DSR

    D6

    Состояние линии RI

    D7

    Состояние линии DCD

    Для передачи байта используется следующая функция:

    На входе: AH = 01h;

    DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3, 3 - COM4;

    AL = передаваемый байт.

    На выходе: AL сохраняется;

    AH = состояние порта асинхронного адаптера, если бит 7 регистра AH установлен в 1, произошла ошибка.

    ______________________________________

    Функция 02h предназначена для приема байта:

    ______________________________________

    На входе: AH = 02h;

    DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3, 3 - COM4;

    На выходе: AL = принятый байт;

    AH = состояние порта асинхронного адаптера, если регистр AH не равен 0, произошла ошибка.

    ______________________________________

    Состояние порта асинхронного адаптера можно узнать с помощью функции 03h:

    ______________________________________

    На входе: AH = 03h;

    DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3, 3 - COM4;

    На выходе: AH = состояние порта асинхронного адаптера;

    AL = состояние модема.

    5.8. Поддержка асинхронного адаптера в MS-DOS

    К сожалению, MS-DOS не содержит сколько-нибудь серьезной поддержки асинхронного адаптера. Две функции прерывания INT 21h с номерами 3 и 4 предназначены для чтения и записи байтов через асинхронный адаптер. Обе эти функции имеют дело с адаптером COM1 или AUX. Функция 3 получает в регистре AL символ, принятый из адаптера, функция 4 посылает в адаптер символ, записанный в регистр DL.

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

    Функции BIOS, обслуживающие адаптер, более разнообразны. Однако и им присущи недостатки. Например, вы не сможете установить скорость передачи более 9600 бит/с или использовать режим фиксации четности. Нет возможности узнать текущий режим асинхронного адаптера, отсутствует поддержка модема.

    Поэтому для программирования асинхронного адаптера мы рекомендуем непосредственно обращаться к портам ввода/вывода микросхемы 8250.

    5.9. Стандартные функции библиотеки Си

    Библиотеки трансляторов Borland C++ и Turbo C содержат две функции управления портами асинхронного последовательного адаптера - bioscom и _bios_serialcom. Обе эти функции обладают одинаковыми возможностями, но функция _bios_serialcom совместима с функцией _bios_serialcom из библиотек трансляторов фирмы Microsoft. Функция bioscom помечена в документации как устаревшая и оставлена для совместимости с ранними версиями трансляторов фирмы Borland.

    Функции _bios_serialcom и bioscom управляют асинхронным последовательным портом компьютера через прерывание BIOS INT 0x14. Вследствие этого функция bioscom может не работать со скоростями больше чем 9600 бит/сек. Если вам нужны программы, обеспечивающие более высокие скорости, необходимо использовать непосредственное программирование портов асинхронного последовательного адаптера.

    Рассмотрим функцию _bios_serialcom более подробно. Она объявлена в файле BIOS.H следующим образом:

    unsigned _bios_serialcom( unsigned service, unsigned serial_port, unsigned data );

    Первый аргумент функции - serial_port - определяет номер порта. Для порта COM1 этот аргумент должен быть равен 0, для COM2 - 1 и так далее.

    Второй аргумент - service - определяет производимое функцией действие и может содержать одну из следующих констант:

    Константа

    Назначение

    _COM_INIT

    Инициализация последовательного порта

    _COM_RECEIVE

    Принять байт

    _COM_SEND

    Передать байт

    _COM_STATUS

    Определить состояние порта

    Назначение третьего аргумента функции - data - зависит от значения аргумента service. Если аргумент service установлен на _COM_RECEIVE или _COM_STATUS, то значение аргумента data безразлично. Если аргумент service установлен как _COM_INIT, то этот аргумент может состоять из одного или нескольких констант, объединенных булевой операцией ИЛИ. Данные константы приведены в следующей таблице:

    Константа

    Назначение

    _COM_CHR7

    Передавать семь битов на символ

    _COM_CHR8

    Передавать восемь битов на символ

    _COM_STOP1

    Использовать один стоповый бит

    _COM_STOP2

    Использовать два стоповых бита

    _COM_NOPARITY

    Не выполнять проверки на четность

    _COM_EVENPARITY

    Выполнять проверку на четность

    _COM_ODDPARITY

    Выполнять проверку на нечетность

    _COM_110

    Установить скорость 110 бит/с

    _COM_150

    Установить скорость 150 бит/с

    _COM_300

    Установить скорость 300 бит/с

    _COM_600

    Установить скорость 600 бит/с

    _COM_1200

    Установить скорость 1200 бит/с

    _COM_2400

    Установить скорость 2400 бит/с

    _COM_4800

    Установить скорость 4800 бит/с

    _COM_9600

    Установить скорость 9600 бит/с

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

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

    Назначение отдельных бит старшего байта представлено в следующей таблице:

    Бит

    Если бит установлен

    15

    Исчерпан лимит времени (тайм-аут)

    14

    Регистр сдвига передатчика свободен (пуст)

    13

    Регистр передатчика свободен (пуст)

    12

    Произошел разрыв связи (состояние BREAK)

    11

    Ошибка в управляющих битах (ошибка синхронизации)

    10

    Ошибка четности

    9

    Ошибка переполнения

    8

    Данные готовы

    Когда аргумент service равен _COM_SEND, бит 15 устанавливается в единицу, если данные не могут быть переданы.

    Если аргумент service равен _COM_RECEIVE и чтение байта произошло успешно, он находится в младшем байте возвращаемого функцией значения. Если чтение произошло с ошибками, это отражается битами 9, 10, 11 или 15.

    Если атрибут service равен _COM_INIT или _COM_STATUS, биты младшего байта определяются следующим образом:

    Бит

    Значение

    7

    Состояние линии DCD

    6

    Состояние линии RI

    5

    Состояние линии DSR

    4

    Состояние линии CTS

    3

    Линия DCD изменила состояние

    2

    Линия RI изменила состояние

    1

    Линия DSR изменила состояние

    0

    Линия CTS изменила состояние

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

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

    Листинг 5.10. Файл SERIAL.C

    // Программа иллюстрирует доступ к последовательному порту
    // через функцию _bios_serialcom()
    
    #include 		// необходимо включить при 
    									// использовании _bios_serialcom()
    #include 
    
    #define COM1					0    		// первый последовательный порт
    #define DATA_READY		0x100		// данные приняты и готовы для
    														// чтения
    int main(void) {
    
    	unsigned in, out, status;
    
    	// Инициализируем последовательный порт
    	// устанавливаем скорость 1200 бит/с, 8 битов на символ, один
    	// стоповый бит
    	_bios_serialcom(	_COM_INIT, COM1, _COM_1200 |
    									_COM_CHR8 | _COM_STOP1);
    
    	printf("\n\n Для выхода нажмите клавишу [ESC]\n");
    
    	for(;;)  {
    
    		// Определяем состояние последовательного порта
    		status = _bios_serialcom(_COM_STATUS, COM1, 0);
    
    		// Если данные готовы, считываем их из
    		// последовательного порта и выводим на экран  дисплея
    		if(status & DATA_READY)
    			if((out = _bios_serialcom(_COM_RECEIVE, COM1, 0) &
    											0x7F) != 0)
    				putch(out);
    
    		// Проверяем, не нажата ли клавиша на клавиатуре?
    		if(kbhit()) {
    
    			// Если нажата клавиша [ESC] выходим из программы
    			if((in = getch()) == 0x1b)
    				break;
    
    			// В противном случае передаем код нажатой клавиши
    			// на асинхронный последовательный порт
    			_bios_serialcom(_COM_SEND, COM1, in);
    		}
    	}
    	return(0);
    }
    

    Теперь рассмотрим функцию bioscom() из библиотеки трансляторов Borland C++ и Turbo C. Эта функция отмечена в документации на Borland C++ версии 4.0, как устаревшая:

    int bioscom(int service, char data, int serial_port);

    Функция аналогична функции _bios_serialcom(), за исключением следующих моментов:

    Отличается порядок следования аргументов функции;

    Не соответствуют типы аргументов, имеющие одинаковый смысл;

    Рассмотрим подробнее аргументы функции bioscom(). Первый аргумент функции - serial_port - определяет номер порта. Для COM1 этот аргумент должен быть равен 0, для COM2 - 1 и так далее.

    Назначение второго аргумента функции - data - зависит от значения аргумента service. Если аргумент service равен единице (_COM_RECEIVE) или тройке (_COM_STATUS), то значение аргумента data безразлично. Если аргумент service равен нулю (_COM_INIT), то этот аргумент может состоять из одного или нескольких битовых полей (констант), объединенных булевой операцией ИЛИ (|). Данные константы приведены в следующей таблице:

    Константа

    Значение

    0x02 (_COM_CHR7)

    Передавать семь битов на символ (байт)

    0x03 (_COM_CHR8)

    Передавать восемь битов на символ

    0x00 (_COM_STOP1)

    Использовать один стоповый бит

    0x04 (_COM_STOP2)

    Использовать два стоповых бита

    0x00 (_COM_NOPARITY)

    Не проводить проверки на четность

    0x18 (_COM_EVENPARITY)

    Проводить проверку на четность

    0x08 (_COM_ODDPARITY)

    Проводить проверку на нечетность

    0x00 (_COM_110)

    Установить скорость 110 бит/с

    0x20 (_COM_150)

    Установить скорость 150 бит/с

    0x40 (_COM_300)

    Установить скорость 300 бит/с

    0x60 (_COM_600)

    Установить скорость 600 бит/с

    0x80 (_COM_1200)

    Установить скорость 1200 бит/с

    0xa0 (_COM_2400)

    Установить скорость 2400 бит/с

    0xc0 (_COM_4800)

    Установить скорость 4800 бит/с

    0xe0 (_COM_9600)

    Установить скорость 9600 бит/с

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

    Третий аргумент - service - может принимать следующие значения:

    Константа

    Значение

    0 (_COM_INIT)

    Инициализация последовательного порта

    1 (_COM_RECEIVE)

    Принять байт

    2 (_COM_SEND)

    Передать байт

    3 (_COM_STATUS)

    Определить состояние порта

    Аналогично функции _bios_serialcom() функция bioscom() возвращает 16-битовое целое число. В старшем байте возвращаемого значения содержатся биты, определяющие состояние последовательного порта. Содержимое младшего байта зависит от значения параметра service, с которым вызывалась функция.

    Возможные значения для старшего байта представлены в следующей таблице:

    Бит

    Если бит установлен

    15

    Исчерпан лимит времени (тайм-аут)

    14

    Регистр сдвига передатчика свободен (пуст)

    13

    Регистр передатчика свободен (пуст)

    12

    Произошел разрыв связи (состояние BREAK)

    11

    Ошибка в управляющих битах (ошибка синхронизации)

    10

    Ошибка четности

    9

    Ошибка переполнения

    8

    Данные готовы

    Если атрибут service равен _COM_INIT или _COM_STATUS, биты младшего байта используются следующим образом:

    Бит

    Значение

    7

    Состояние DCD линии

    6

    Состояние RI линии

    5

    Состояние DSR линии

    4

    Состояние CTS линии

    3

    Линия DCD изменила состояние

    2

    Линия RI изменила состояние

    1

    Линия DSR изменила состояние

    0

    Линия CTS изменила состояние

    [Назад] [Содеожание] [Дальше]

  •