Анатомия COM-порта

В последнее время последовательный способ передачи данных вытесняет параллельный.
За примерами далеко ходить не надо: появление шин USB и SATA говорит само за себя.
И действительно, параллельную шину трудно масштабировать (удлинить шлейф, увеличить частоту тактирования шины), неудивительно, что технологии поворачиваются к параллельным шинам задней частью.

Последовательные интерфейсы

На сегодняшний день существует великое множество различных интерфейсов последовательной передачи данных.
Кроме уже упомянутых USB и SATA еще можно вспомнить как минимум два широко известных стандарта RS-232 и MIDI (он же и GamePort).
Объединяет их все то же — последовательная передача каждого бита информации, или Serial Interface.
Преимуществ у подобных интерфейсов великое множество, и самое главное из них — малое количество соединительных проводов, а следовательно, меньшая цена.

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

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

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

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

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

Ну, если бы все компьютерные технологии были просты, то любая домохозяйка давно бы уже лепила параллельно с пельменями новые протоколы …
Попробуем взглянуть на процесс по-другому.
Данные передаются пакетами, примерно как IP пакеты, вместе с данными идут и информационные биты, количество этих битов может варьироваться от 2 до 3 с половиной.
С половиной?!
Да, ты не ослышался, именно с половиной!

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

Такой способ передачи подразумевает, что приемник и передатчик должны работать с одной скоростью (ну, или почти с одной), иначе пришедшие биты данных приемник будет либо не успевать обрабатывать, либо принимать старый бит за новый.
Для того чтобы этого избежать, каждый бит стробируется, то есть посылается синхронно со специальным сигналом — «стробом», формируемым внутри прибора.
Существует ряд определенных скоростей работы асинхронных устройств — 50, 75, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 19 200, 38 400, 57 600 и 115 200 бит в секунду.

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

Теперь пару слов о загадочном термине «пакет данных».
Под пакетом в данном случае понимается набор битов, передаваемых между стартовым и стоповым битами.
Их число может изменяться от пяти до восьми.
Можно задаться вопросом, почему именно пять-восемь бит?
Почему бы не передать сразу, скажем, килобайт данных внутри пакета?

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

В качестве примера асинхронного последовательного устройства передачи данных можно привести COM-порт компьютера, любимый модем с дизайном от Труссарди и мышь, подключаемую к этому же порту, которую недалекие секретарши почему-то все время стараются засунуть в PS/2.
Работают все эти устройства по интерфейсу RS-232, вернее по асинхронной его части, поскольку в стандарте описана и синхронная передача данных.

RS-232 электрический интерфейс

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

Длина соединительных проводов не должна превышать 15 метров.
Логической единице соответствует значение напряжения на входе приемника от -12 до -3 Вольт, логическому нулю — от +3 до +12 Вольт.
На выходе передатчика, соответственно, логическая единица — это напряжение в диапазоне от -12 до -5 Вольт, а логический ноль — от +5 до +12 Вольт.

Что касается разъема, то ты наверняка знаешь, что бывают 9-контактные и 25-контактные разъемы для последовательных интерфейсов, так вот, 25-контактный разъем как раз предназначен для синхронной работы, а 9-ти — для асинхронной.
COM-порт компа выглядит как разъем типа «папа» (male), ну а подключаемая аппаратура имеет разъем типа «мама» (female).

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

COM-порт

На самой первой персоналке COM-порт уже был, поэтому он может похвастаться как минимум двадцатилетней историей.
Хорошо это или плохо?
С точки зрения скорости работы наверное плохо, поскольку это старый стандарт, и скорость передачи данных по нему не может превышать 115200 бит в секунду, что, согласись, по сегодняшним временам очень и очень мало.
Допустим, если с такой скоростью копировать фильм в формате DivX, то процесс будет завершен всего-то за 16 часов.

Однако есть и другая точка зрения: поскольку это старый стандарт, то в реализации найдены и исправлены практически все ошибки, по стандарту написано множество документации и имеется огромное количество программного кода, использующего этот стандарт.
Стандарт широко распространен — сегодня невозможно найти персональный компьютер без COM-порта, а значит устройство, подключаемое к нему, может быть использовано в любой точке планеты.
Кроме того производители железа активно используют COM-порт для вспомогательных целей (например, для подключения ИК-приемника пульта ДУ), да и свое самодельное устройство легче всего подключить именно к COM-порту.

Мышь и COM-порт

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

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

• RD (2) — для передачи данных;
• SG (5) — земля;
• DTR, RTS (4, 7) — положительное питание;
• TD (3) — отрицательное питание.

Существует две разновидности мышей, подключаемых к COM-порту — MS Mouse и PC Mouse.
Каждая из этих мышей должна использовать свой драйвер, поскольку они, хотя и работают на одной скорости — 1200 бит/с, используют один стоповый бит и не используют контроль четности, но имеют различный формат передаваемых данных.

MS Mouse передает с каждым пакетом по 7 бит информации, а PC Mouse — 8.
Кроме того, состояние MS Mouse описывается тремя байтами, а PC Mouse — пятью.

Подключение модема

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

Программирование COM-порта

Каждый COM-порт однозначно ассоциируется с двумя параметрами – базовым адресом порта и номером прерывания.
В BIOS’е ты можешь выбрать, какие именно значения соответствуют каждому порту.
По умолчанию там указаны следующие значения для портов: COM1 — 3F8-IRQ4, COM2 — 2F8-IRQ3.
Управляется порт семью регистрами, с адресами ввода/вывода начиная с 3F8 по 3FE (для COM1) или с 2F8 по 2FE (для СОМ2) — это в том случае, если ты используешь базовый адрес порта по умолчанию.
Для программирования COM-порта нужно записывать или считывать значения в порты по этим адресам.
Начнем по порядку.

Назначение регистров

Итак, регистр данных — ему соответствует адрес порта ввода/вывода 3F8.
Записывая в этот порт данные, ты сообщаешь передатчику байт, который необходимо послать.
Передатчик сохраняет этот байт в своем буфере данных (регистре) и в дальнейшем передает его приемнику по интерфейсу.
В случае приема данных этот же регистр используется для чтения приятого байта.
Тем не менее, передача данных — не единственное назначение этого регистра.

Если в регистре управления портом (3FB) седьмой бит выставлен в единицу, то регистр 3F8 выдает тебе младший байт делителя частоты тактового генератора, старший байт в этом случае выводится в регистре 3F9.
Получившееся слово характеризует скорость работы COM-порта.

Регистр управления прерываниями (3F9) разрешает (бит равен 1), или запрещает (бит равен 0) следующие прерывания:

• бит 0 — готовность принимаемых данных;
• бит 1 — готовность переданных данных (когда записанный в регистр данных байт, целиком передан);
• бит 2 — состояние разрыва соединения или обнаружения ошибки;
• бит 3 — изменения на разъеме COM-порта.

Регистр идентификации прерывания (3FA) определяет причину появления прерывания.
Бит ноль, если он равен единице, сигнализирует об отсутствии прерываний, ожидающих обслуживание.
В битах 1-2 зашифрованы следующие состояния:

• 00 — прерывание генерируется при переполнении приемника, ошибке четности или формата данных, или при состоянии разрыв соединения, сбрасывается при чтении регистра состояния линии;
• 01 — данные приняты и доступны для чтения, сбрасывается после чтения из порта данных;
• 11 — устанавливается при изменении состояния входных линий CTS, RI, DCD и DSR.

Биты 3-7 должны быть равны нулю.

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

Регистр управления устройством модуляции-демодуляции или просто модемом (3FC) — достаточно специфический регистр, который управляет состоянием линий DTR (бит 0) и RTS (бит 1), состоянием модемных резервных линий OUT1 (бит 2), OUT2 (бит 3) и запуском автодиагностики порта при замыкании выхода на вход (бит 4).
Биты с пятого по седьмой должны быть равны нулю.
Выставление соответствующего бита в 1 приводит к появлению на линии логической единицы, запуск автодиагностики происходит также при выставлении четвертого бита в единицу.

Регистр состояния линии (3FD) является самым полезным регистром после регистра данных.
С его помощью ты всегда сможешь узнать, в каком состоянии у тебя находится порт, и можно ли тебе произвести нужное действие.
Вот формат этого регистра (1 соответствует активному состоянию):

• бит 0 — приемник получил данные, и их можно прочитать из регистра данных, при чтении из регистра 3F8 этот бит устанавливается в 0;
• бит 1 — потеря данных, новый байт данных был получен, а старый не прочитан из регистра данных, и новый байт заместил в регистре данных старый байт;
• бит 2 — произошла ошибка четности;
• бит 3 — произошла ошибка синхронизации;
• бит 4 — обнаружен разрыв соединения;
• бит 5 — регистр переданных данных пуст, можно записать новый байт;
• бит 6 — данные переданы приемнику (то есть сдвиговый регистр, куда помещается байт из регистра данных, пуст);
• бит 7 — устройству не удалось связаться с компьютером в установленный срок.

И, наконец, последний из регистров COM-порта, регистр состояния модема (3FE), имеет следующий формат:

• бит 0 — произошло изменение состояния линии CTS;
• бит 1 — произошло изменение состояния линии DSR;
• бит 2 — произошло изменение состояния линии IR;
• бит 3 — произошло изменение состояния линии DCD;
• бит 4 — состояние линии CTS;
• бит 5 — состояние линии DSR;
• бит 6 — состояние линии IR;
• бит 7 — состояние линии DCD.

Алгоритм работы

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

Вначале необходимо инициализировать порт.
Для этого воспользуйся регистром управления 3FB и установи нужные тебе параметры порта, а также реши, нужны ли тебе прерывания.
Если нужны, то твоя дверца — с надписью 3F9.

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

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

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

Наконец последнее, о чем я хочу тебя предупредить, это различия в работе операционных систем с ядром NT (Windows NT, 2000, XP, AS2003) и старых Windows 9Х/Me.
Если в старых ОС можно было напрямую работать с портами, например с помощью методов стандартной библиотеки C — _inp(), _outp(), которые, в свою очередь, просто выполняют ассемблерные команды in, out, то в новых системах тебе придется изрядно попотеть.
По словам Microsoft, для целостности системы больше кого попало к портам ввода/вывода не пускают, и если ты хочешь что-нибудь прочитать или записать из/в порт, будь любезен, работай на уровне драйвера устройства, то есть пиши свой драйвер, дружок.

Вот так, не порт, а целый детектив.
Если столько премудростей скрывает в своей работе древний СОМ-порт, то что же говорить о более современных интерфейсах?

Автор: Антон Палагин

^