Аннотация
Книга представляет собой новую редакцию четвертого тома "Библиотеки системного программиста" и посвящена использованию модемов и факс-модемов. В ней рассмотрены различные типы современных модемов, описаны их команды и регистры. Приведены рекомендации по покупке и установке модема.
Описана процедура установки и настройки нескольких наиболее распространенных телекоммуникационных программ. Вы научитесь передавать и принимать документы через модемы и факс-модемы.
В книге описаны приемы программирования портов асинхронного последовательного адаптера и модемов в среде операционных систем MS-DOS и Windows, приведены несколько примеров программ.
Аппаратная реализация
Обычно компьютер оснащен одним или двумя портами последовательной передачи данных. Эти порты расположены либо на материнской плате, либо на отдельной плате, вставляемой в слоты расширения материнской платы.
Бывают специальные платы, содержащие четыре или восемь портов последовательной передачи данных. Их часто используют для подключения нескольких модемов и факс-модемов к одному центральному компьютеру.
Сердцем последовательного асинхронного адаптера служит микросхема универсального асинхронного приемопередатчика (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 со стороны последовательного асинхронного адаптера:
Номер контакта | Назначение контакта
| Вход или выход | |||
1 | Защитное заземление (Frame Ground, FG) | - | |||
2 | Передаваемые данные (Transmitted Data, TD) | Выход | |||
3 | Принимаемые данные (Received Data, RD) | Вход | |||
4 | Запрос для передачи (Request to send, RTS) | Выход | |||
5 | Сброс для передачи (Clear to Send, CTS) | Вход | |||
6 | Готовность данных (Data Set Ready, DSR) | Вход | |||
7 | Сигнальное заземление (Signal Ground, SG) | - | |||
8 | Детектор принимаемого с линии сигнала (Data Carrier Detect, DCD). Иногда сигнал DCD обозначают как CD (Carrier Detect) или RLSD (Receive Line Signal Detect) | Вход | |||
9-19 | Не используются | ||||
20 | Готовность выходных данных (Data Terminal Ready, DTR) | Выход | |||
21 | Не используется | ||||
22 | Индикатор вызова (Ring Indicator, RI) | Вход | |||
23-25 | Не используются |
Теперь приведем разводку разъема DB9 со стороны последовательного асинхронного адаптера:
Номер контакта |
Назначение контакта |
Вход или выход |
1 |
Детектор принимаемого с линии сигнала (Data Carrier Detect, DCD). Иногда сигнал DCD обозначают как CD или RLSD |
Вход |
2 |
Принимаемые данные (Received Data, RD) |
Вход |
3 |
Передаваемые данные (Transmitted Data, TD) |
Выход |
4 |
Готовность выходных данных (Data Terminal Ready, DTR) |
Выход |
5 |
Сигнальное заземление (Signal Ground, SG) |
- |
6 |
Готовность данных (Data Set Ready, DSR) |
Вход |
7 |
Запрос для передачи (Request to send, RTS) |
Выход |
8 |
Сброс для передачи (Clear to Send, CTS) |
Вход |
9 |
Индикатор вызова (Ring Indicator, RI) |
Вход |
Интерфейс RS-232-C определяет обмен между устройствами двух типов: DTE (Data Terminal Equipment - терминальное устройство) и DCE (Data Communication Equipment - устройство связи). В большинстве случаев, но не всегда, компьютер является терминальным устройством. Модемы, принтеры, графопостроители всегда являются устройствами связи. Рассмотрим теперь сигналы интерфейса RS-232-C более подробно.
Асинхронная и синхронная передача данных
Последней характеристикой, которую мы рассмотрим, определяет, какой применяется способ передачи информации между модемами - асинхронный или синхронный.
В асинхронном режиме информация передается следующим образом. Каждый передаваемый байт предваряется стартовым битом и заканчивается одним или двумя стоповыми битами. Иногда также передается дополнительный бит четности, используемый для проверки целостности передаваемого байта.
В синхронном режиме данные передаются одним потоком. Байт за байтом, бит за битом. Стартовые и стоповые биты отсутствуют. Поэтому при одинаковой скорости в синхронном режиме можно передать больше полезной информации, чем в асинхронном.
Асинхронный адаптер
Практически каждый компьютер оборудован хотя бы одним асинхронным последовательным адаптером. Обычно он представляет собой отдельную плату или же расположен прямо на материнской плате компьютера. Его иногда называют асинхронным адаптером RS-232-C, или портом RS-232-C. Асинхронный адаптер обычно содержит несколько COM-портов, через которые к компьютеру можно подключать внешние устройства.
Каждому COM-порту соответствует несколько регистров, через которые программа получает к нему доступ, и определенная линия IRQ для сигнализирования компьютеру об изменении состояния порта. На этапе инициализации модули BIOS присваивают каждому COM-порту уникальный номер. Например, компьютер может иметь четыре порта COM1 - COM4.
В настоящее время порт последовательной передачи данных используется очень широко. Чаще всего к нему подключается манипулятор мышь. Иногда через COM-порт к компьютеру подключаются графопостроители, сканеры, принтеры, дигитайзеры.
Для нас наиболее интересно использование COM-портов для обмена данных между двумя компьютерами через модемы и нуль-модемы.
Если компьютеры расположены близко друг от друга, то их можно связать с помощью специального кабеля, называемого нуль-модемом. Этот кабель подключается к COM-портам обоих компьютеров и позволяет организовать обмен данными с высокими скоростями - до 115000 бит/с.
Модем также подключается к компьютеру через COM-порт и позволяет обмениваться данными по обычным телефонным линиям.
Bell 103, Bell 212A
Bell - устаревший американский стандарт, не совместимый со стандартами CCITT. Регламентирует низкоскоростные модемы, передающие данные со скоростями 300 бит за секунду и 1200 бит за секунду в дуплексном режиме.
Биты за секунду
Модем обменивается данными с компьютером через порты асинхронного последовательного адаптера. Данные передаются последовательно бит за битом. Скорость, с которой происходит этот обмен, измеряется в битах за секунду (бит/с), или bps. Обычно, когда речь идет о скорости, с которой модем может передавать информацию, используется термин бит за секунду.
Например, вы можете встретить такие обозначения скорости передачи информации в документации модема - 1200 bps, 2400 bps, 9600 bps, 14400 bps. При этом подразумевается скорость, с которой происходит передача данных между компьютером и модемом.
Боды
Скорость в бодах определяется числом изменений сигнала, передаваемым модемом по телефонной линии, произошедшего за одну секунду. Различные способы модуляции могут кодировать в одном значении сигнала несколько бит. Поэтому значение скорости передачи данных модемом в бодах может отличаться от значения скорости в битах за секунду.
Так, для модемов, соответствующих рекомендации CCITT V.32 и работающих со скоростью 9600 bps, применяется комплексный метод модуляции, в котором информация кодируется одновременным изменением фазы и амплитуды. Это позволяет закодировать в каждом значении сигнала 4 бита полезной информации. Таким образом, модем, работающий со скоростью 9600 bps, передает данные со скоростью всего 2400 бод.
Боды, биты за секунду и символы в секунду
Теперь настало время поговорить о том, как измеряется скорость передачи информации. В литературе и документации модемов фигурируют три различных единицы измерения скорости: биты за секунду (Bits Per Second - bps), боды (baud) и символы за секунду (Characters Per Second - cps). К сожалению, среди этих терминов существует путаница. Многие пользователи и даже сами разработчики модемов не делают разницы между этими понятиями. Мы придерживаемся в своем изложении следующих соглашений.
Буфер передатчика пуст
Прерывание происходит в случае, если буфер передатчика пуст и можно передать COM-порту очередной символ. Можно организовать буфер передатчика программы, в который программа будет записывать данные, предназначенные для передачи через COM-порт. В этом случае, когда придет прерывание, надо считать очередной символ из буфера передатчика программы и записать его в регистр данных. Прерывание сбрасывается после записи очередного символа в регистр данных UART. Если нет данных для передачи (программный буфер передатчика пуст), можно запретить это прерывание через регистр управления прерываниями.
регламентированный данной рекомендацией, предназначен для
Модем, регламентированный данной рекомендацией, предназначен для передачи данных по телефонным линиям. Он работает в асинхронном дуплексном режиме. Для передачи и приема данных используется метод частотной модуляции. Передача данных происходит с крайне низкой скоростью - до 300 бит за секунду.
Протокол V.21 используется факсимильными аппаратами группы 3 и факс-модемами на этапе установления связи. Сама передача факсимильных сообщений происходит уже в соответствии с другими, более эффективными рекомендациями.
синхронный дуплексный режим передачи данных.
Модем, работающий в соответствии с данной рекомендацией, использует асинхронно- синхронный дуплексный режим передачи данных. Асинхронно-синхронный режим означает, что компьютер передает модему данные в асинхронном режиме. Модем удаляет из потока данных компьютера стартовые и стоповые биты. И далее, уже в синхронном виде, передает их удаленному модему.
Передача данных может происходить со скоростями 600 и 1200 бит за секунду. Для модуляции передаваемого сигнала используется метод фазовой модуляции.
CCITT V.22 bis
Дуплексный модем, со скоростью передачи данных 1200 и 2400 бит за секунду. При передаче данных со скоростью 2400 бит за секунду используется метод квадратурно-амплитудной модуляции, а при скорости 1200 бит за секунду - метод фазовой модуляции.
Модем, регламентированный CCITT V.22 bis может обмениваться данными с модемами соответствующими рекомендации CCITT V.22. Естественно скорость обмена не будет выше, чем 1200 бит за секунду.
Асинхронный модем, использующий метод частотной
Асинхронный модем, использующий метод частотной модуляции. Модем может работать в полудуплексном режиме со скоростью передачи данных по прямому каналу - 600/1200 бит за секунду, а по обратному - только 75 бит за секунду. Отметим, что этот стандарт не совместим с CCITT V.21, V.22 и V.22 bis.
синхронный дуплексный режим передачи данных.
Модем, работающий в соответствии с данной рекомендацией, использует асинхронно- синхронный дуплексный режим передачи данных. Для модуляции передаваемого сигнала применяется метод квадратурно-амплитудной модуляции. Позволяет передавать данные со скоростями 4800 и 9600 бит за секунду.
CCITT V.32 bis
V.32 bis является расширением рекомендации V.32. Модем, работающий в соответствии с данной рекомендацией, использует асинхронно-синхронный дуплексный режим передачи данных. Для модуляции передаваемого сигнала применяется метод квадратурно-амплитудной модуляции. Позволяет передавать данные со скоростями 7200, 12000 и 14400 бит за секунду.
могут передавать данные по телефонным
Модемы, соответствующие новой рекомендации V.34, могут передавать данные по телефонным каналам с рекордной на настоящий момент скоростью 28800 бит за секунду. В дополнение к высокой скорости в рекомендации V.34 применяется новый протокол установления связи с удаленным модемом. За счет этого модемы, поддерживающие V.34, могут соединяться значительно быстрее.
Чтение блока данных из буфера драйвера
Позволяет за один вызов функции считать сразу несколько байт из приемного буфера драйвера в буфер программы.
На входе: AH = 18h;
DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3,
3 - COM4 и т. д.;
CX = максимальное количество считываемых символов;
ES:DI - адрес буфера, в который помещаются
считанные символы.
На выходе: AX = количество считанных символов.
Чтение и запись в регистры модема (команда S)
Команда S имеет два различных формата Sr? и Sr= n. Команда Sr? - производит чтение содержимого регистра модема, имеющего номер r. Команда Sr = n - записывает число n в регистр модема, имеющий номер r. Число n может принимать значения от 0 до 255.
Все AT-команды модифицируют содержимое одного или более S-регистров. Некоторые S-регистры содержат временные параметры, которые можно поменять только командой S. Наиболее интересным представляется регистр S0, позволяющий включить режим автоответа на вызов удаленного модема.
Например, чтобы перевести модем в режим автоответа, ему необходимо передать следующую команду:
ATS0=0 <CR>
В ответ на денную команду, модем запишет в регистр S0 значение 0 и вернет компьютеру сообщение OK.
Чтение символа из буфера без удаления
Функция 0Ch предназначена для чтения очередного символа из буфера приемника драйвера. При этом прочитанный символ из буфера не удаляется.
На входе: AH = 0Ch;
DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3,
3 - COM4 и т. д.
На выходе: если AH = 0, то регистр AL содержит принятый
байт;
если AX = 0FFFFh, то буфер приемника пуст.
Если буфер приемника пуст, функция ожидает поступления очередного байта из COM-порта.
Чтение символа из буфера клавиатуры
Данная функция обеспечивает ввод с клавиатуры без ожидания. Если буфер клавиатуры пуст - функция возвращает в регистре AX значение 0FFFFh. В противном случае скан-код очередного символа, прочитанный из буфера клавиатуры, помещается в регистр AH. Заметим, что функция не удаляет код прочитанного ей символа из буфера клавиатуры.
На входе: AH = 0Dh.
На выходе: AX = 0FFFFh - буфер клавиатуры пуст;
AX = скан-код нажатой клавиши.
Чтение символа из буфера клавиатуры с ожиданием
Функция 0Eh производит чтение кода очередного символа из буфера клавиатуры. Если буфер клавиатуры пуст, функция переходит в режим ожидания до тех пор, пока не будет нажата какая-нибудь клавиша.
На входе: AH = 0Eh.
На выходе: AX = скан-код нажатой клавиши.
Чтение текущей конфигурации модема (команда &V)
По команде &V модем показывает свою текущую конфигурацию и телефонные номера, записанные в энергонезависимой памяти:
AT&V
ACTIVE PROFILE:
B1 E1 L1 M1 N1 Q0 T V1 W0 X4 Y0 &C1 &D0 &G0 &J0 &K3 &Q5 &R1 &S0 &T4 &X0 &Y0
S00:000 S01:000 S02:043 S03:013 S04:010 S05:008 S06:002 S07:050 S08:002 S09:006
S10:014 S11:095 S12:050 S18:000 S25:005 S26:001 S36:007 S37:000 S38:020 S44:020
S46:138 S48:007 S95:000
STORED PROFILE 0:
B1 E1 L1 M1 N1 Q0 T V1 W0 X4 Y0 &C1 &D0 &G0 &J0 &K3 &Q5 &R1 &S0 &T4 &X0
S00:000 S02:043 S06:002 S07:050 S08:002 S09:006 S10:014 S11:095 S12:050 S18:000
S36:007 S37:000 S40:105 S41:131 S46:138 S95:000
STORED PROFILE 1:
B1 E1 L1 M1 N1 Q0 T V1 W0 X4 Y0 &C1 &D0 &G0 &J0 &K3 &Q5 &R1 &S0 &T4 &X0
S00:000 S02:043 S06:002 S07:050 S08:002 S09:006 S10:014 S11:095 S12:050 S18:000
S36:007 S37:000 S40:105 S41:131 S46:138 S95:000
TELEPHONE NUMBERS:
0=123 45 67 1=987 65 43
2=102 93 84 3=564 73 82
OK
Деинициализация драйвера
Эта функция сообщает драйверу, что программа закончила работу с последовательным адаптером.
На входе: AH = 05h;
DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3,
3 - COM4 и т. д.
На выходе: не используется.
Дополнительная функция для управления потоком
На входе: AH = 10h;
AL = способ управления потоком:
D0 - Если данный бит равен единице, то
полученные через COM-порт символы
Ctrl-C/K не записываются в буфер
приемника, а устанавливают внутренний
флаг. Следующий вызов функции будет
возвращать в регистре AX значение
этого флага.
D1 - Если данный бит равен единице,
процесс передачи данных драйвером
останавливается. Если D1 равен нулю,
передача возобновляется.
DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3,
3 - COM4 и т. д.
На выходе: AX = 01h - были получены символы Ctrl-C/K;
AX = 01h - символы Ctrl-C/K не были получены.
Драйвер асинхронного последовательного адаптера
Драйвер асинхронного последовательного адаптера представляет собой специальную библиотеку динамической компоновки. Он экспортирует набор функций, которые Windows может использовать для того, чтобы открыть порт, установить новый режим его работы, передать и принять данные, определить состояние порта и причину возникновения ошибок.
Однако приложения не обращаются непосредственно к этим функциям. Вместо этого они обращаются к функциям, экспортируемым из модуля USER, операционной системы Windows - OpenComm, CloseComm и т. д. Подробное описание данных функций представлено в следующем разделе книги.
Функции из модуля USER обращаются уже непосредственно к драйверу асинхронного последовательного адаптера.
В состав операционных систем Windows 3.1 и Windows for Worksgroups 3.11 входит драйвер асинхронного последовательного адаптера COMM.DRV. Этот драйвер позволяет получить доступ к портам COM1, COM2, COM3 и COM4. При этом драйвер использует для COM-портов адреса, записанные в области переменных (данных) BIOS.
Если область переменных BIOS не содержит информации о COM-портах, то базовые адреса и номера линии IRQ пользователь должен установить самостоятельно при помощи приложения Control Panel.
Введенная вами информация записывается в директивах COMxBase и COMxIRQ из раздела [386Enh] файла SYSTEM.INI. Данные из этих директив используются только в случае, когда в области переменных BIOS нет данных.
Драйвер асинхронного последовательного адаптера определяет тип микросхемы UART, на основе которой построен асинхронный последовательный адаптер. Если он обнаруживает UART 16550A, то включает аппаратный буфер FIFO, расположенный на нем.
В этом случае Windows устойчиво работает на скоростях 9600 бит/с и выше. Многие компьютеры, на которых не установлена эта микросхема, теряют информацию во время передачи данных и не могут установить связь на скорости выше 9600 бит/с.
Дуплексный и полудуплексный режимы
Дуплексный режим работы модема позволяет одновременно передавать данные в двух направлениях. В дуплексном режиме работают модемы, соответствующие рекомендациям CCITT V.21, V.22, V.22 bis и V.32.
Полудуплексный режим, так же как и дуплексный, позволяет передавать данные в обоих направлениях, но только в разные моменты времени. Сначала данные передаются в одну сторону, а затем в обратную. Таким образом, модем сможет и принимать и передавать данные, однако по сравнению с дуплексным модемом при одинаковой скорости передачи полудуплексный сможет передать в два раза меньше данных.
В общем случае дуплексные протоколы обладают большей производительностью, чем полудуплексные. Тем не менее, для тех приложений, в которых основной поток данных передается в одном направлении, вполне можно воспользоваться полудуплексными модемами. Так, практически все протоколы, используемые в факс-модемах для передачи факсов, полудуплексные: V.17, V.27 bis, V.27 ter, V.29.
Escape-последовательность
Escape-последовательность +++ используется для перевода модема из режима передачи данных в командный режим. Благодаря этой команде можно перейти из режима передачи данных модемом в командный режим работы без разрыва связи. Модем требует тишины перед и после направления этой Escape-последовательности. Величина этого промежутка тишины определена в регистре S12. В заводской установке регистр S12 содержит значение 50, соответствующее 1 секунде. Десятичную величину знака ASCII, который является знаком последовательности выхода, содержит регистр S2 (в заводской установке S2 = 43, т.е. '+').
После того как модем перешел в командный режим, можно повесить трубку и разорвать сеанс связи. Для этого следует передать модему команду ATH0 <CR>.
Если вы перевели модем в командный режим, то чтобы изменить некоторые параметры связи вы можете переключить его обратно в режим передачи данных. Для этого можно передать модему команду ATO <CR>.
Операционная система Windows for Workgroups
Операционная система Windows for Workgroups 3.11 включает в себя приложение Microsoft At Work PC Fax, предназначенное для работы с факс-модемами. Оно позволяет передавать факсимильные сообщения, подготовленные различными приложениями Windows. Microsoft At Work PC Fax делает посылку факсимильного сообщения такой же простой, как печать документа на принтере.
Сначала мы расскажем о том, как подключить факс-модем. Запустите приложение Control Panel. На экране компьютера появится окно "Control Panel" (см. рис. 4.15).
Рис. 4.15. Окно "Control Panel"
|
В окне "Control Panel" вы увидите пиктограмму, имеющую название "Fax", которой не было в операционной системе Windows 3.1. Приложение, имеющее эту пиктограмму, позволяет управлять факс-модемом, подключенным к компьютеру. |
Рис. 4.16. Диалоговая панель "Fax Modems"
Чтобы подключить новый факс-модем, нажмите в диалоговой панели "Fax Modems" на кнопку "Add...". На экране появится новая диалоговая панель "Add Fax Modems" (см. рис. 4.17). В ней расположен список "Type". Этот список содержит четыре строки "Fax Modem on COM1", "Fax Modem on COM2", "Fax Modem on COM3", "Fax Modem on COM4", "CAS Fax Modem" и "Shared Network Fax".
Если вы имеете факс-модем, подключенный к какому-либо COM-порту вашего компьютера, выберите соответствующую строку. Например, если вы имеете факс-модем, подключенный к порту COM2, выберите строку "Fax Modem on COM2".
В случае если вы обладаете факс-модемом CAS, который не подключается через COM-порт, выберите строку "CAS Fax Modem".
И наконец, в том случае, если вы работаете в сети и факс-модем установлен на другом компьютере, выберите строку "Shared Network Fax".
Рис. 4.17. Диалоговая панель "Add Fax Modems"
После выбора строки из списка "Type" нажмите на кнопку "OK". Приложение проверит, действительно ли к компьютеру подключен факс-модем. Затем диалоговая панель "Add Fax Modems" будет закрыта, а в списке "Installed Modems", диалоговой панели "Fax Modems" появится новая строка (см. рис. 4.18). В нашем примере список "Installed Modems" пополнится строкой "Fax Modem on COM2".
Если к компьютеру подключено несколько факс-модемов, выберите из списка "Installed Modems" тот факс-модем, который вы будете использовать, и нажмите кнопку "Set as Active Modem".
Рис. 4.18. Диалоговая панель "Fax Modems"
Теперь можно настроить факс-модем. Для этого следует нажать кнопку "Setup...". После этого на экране появится диалоговая панель "Fax Modem on COM2" (см. рис. 4.19). В группе "Fax Modem Number" введите телефонный номер вашего факс-модема. В поле "Country Code" следует ввести код страны, в поле "Area Code" - код региона, а в поле "Phone Number" - телефонный номер факс-модема.
В окне "Answer Mode" содержится трехпозиционный переключатель, управляющий режимом ответа факс-модема на приходящие телефонные звонки. Первая позиция переключателя - "Off" - означает, что факс-модем не будет отвечать на приходящие звонки и не станет принимать факсимильные сообщения от удаленного модема.
Если вы желаете вручную отвечать на приходящие вашему факс-модему вызовы, установите переключатель "Answer Mode" в положение "Manual".
Чтобы компьютер автоматически отвечал на приходящие вызовы и принимал факсимильные сообщения, установите переключатель "Answer Mode" в положение "Auto Answer". Если вы выбрали этот режим, необходимо в поле "Number of Rings" указать количество звонков, после которого факс-модем снимет трубку и установит связь.
Рис. 4.19. Диалоговая панель "Fax Modem on COM2"
В диалоговой панели " Fax Modem on COM2" находятся еще две полезные кнопки - "Speaker..." и "Dialing...". После нажатия на кнопку "Speaker..." на экране появится диалоговая панель "Fax Modem Speaker Control" (см. рис. 4.20).
Рис. 4.20. Диалоговая панель "Fax Modem Speaker Control"
В этой диалоговой панели можно указать, когда будет включаться динамик факс-модема, и задать громкость звуковых сигналов. Если вам надо, чтобы динамик факс-модема был постоянно отключен, переведите переключатель "Speaker Mode" в положение "Off". Если вы желаете иметь постоянный контроль за факс-модемом, установите этот переключатель в положение "On Always". И наконец, если вы желаете, чтобы динамик был включен только на время набора номера, установите переключатель "Speaker Mode" в положение "On During Dial".
Громкость звукового сигнала динамика факс-модема можно регулировать с помощью полосы просмотра "Volume". Чем левее находится ползунок полосы просмотра, тем тише работает динамик.
Теперь нажмите кнопку "Dialing..." в диалоговой панели "Fax Modem on COM2". На экране появится новая диалоговая панель "Modem Dialing Options" (см. рис. 4.21). Наиболее важно в этой диалоговой панели установить переключатель "Line Type" в положение "Pulse". Это будет означать, что для набора номера будет использоваться импульсная система. Все отечественные АТС работают только с импульсной системой набора номера.
Рис. 4.21. Диалоговая панель "Modem Dialing Options"
Остальные поля диалоговой панели можно оставить без изменения. Закройте все открытые диалоговые панели, нажимая на кнопку "OK". Вы вернетесь в окно приложения "Control Panel", которое также можно закрыть. Теперь у вас появился еще один принтер - "Microsoft At Work PC Fax".
|
Если вы еще не главное окно "Control Panel", запустите приложение Printers. На экране появится диалоговая панель "Printers". |
FOSSIL-драйвер и эмуляция протокола MNP
Кроме буферизации передаваемых и принимаемых данных некоторые FOSSIL-драйверы выполняют еще одну функцию. При работе с модемами, не реализующими протокол аппаратной коррекции ошибок, они обеспечивают программную эмуляцию протокола MNP.
Примером таких драйверов могут служить FOSSIL-драйверы MX5 Version 1.02 - MNP Level 5 Driver и MNP Version 1.27 - MNP Level 5 Driver.
Для управления эмулятором MNP FOSSIL-драйверы, реализующие программную эмуляцию, поддерживают дополнительную функцию номер 0E0h прерывания INT 14h. Ниже мы приведем краткое описание подфункций данной функции.
Функция BuildCommDCB
Функция BuildCommDCB заполняет структуру DCB в соответствии с переданной ей строкой параметров. Структура DCB содержит управляющую информацию, необходимую для установки режима работы портов последовательного асинхронного адаптера (см. раздел "Структура DCB").
int BuildCommDCB(LPCSTR lpszDef, DCB FAR* lpdcb);
Параметр lpszDef является дальним указателем на строку символов, закрытую двоичным нулем, которая должна содержать команды установки режима COM-порта. Формат этой строки полностью соответствует параметрам команды MODE операционной системы MS-DOS.
Например, параметр lpszDef может указывать на строку "COM1:9600,n,8,1". В этом случае после вызова функции BuildCommDCB заполненная структура DCB позволит установить порт COM1 в режим обмена данными со скоростью 9600 бит/с, без проверки на четность с форматом передаваемых символов из 8 бит данных и одним стоповым битом.
Параметр lpdcb является дальним указателем на структуру типа DCB. В нее будет записана информация, полученная после интерпретации строки lpszDef.
Функция BuildCommDCB возвращает ноль, если команды, указанные в параметре lpszDef, успешно интерпретированы и структура DCB заполнена. В случае возникновения ошибки, например, в формате команд, функция возвращает -1.
Функция BuildCommDCB только заполняет поля структуры DCB. Чтобы установить режим порта последовательного асинхронного адаптера в соответствии с заполненной структурой DCB, необходимо воспользоваться функцией SetCommState.
С помощью функции BuildCommDCB вы можете управлять далеко не всеми характеристиками COM-порта, определяемыми структурой DCB. Так как формат команд строки lpszDef соответствует команде MODE MS-DOS, то с помощью BuildCommDCB можно изменить только скорость передачи информации, режим проверки на четность и формат передаваемых данных (число стоповых бит и бит данных в передаваемых символах).
Остальные поля структуры DCB функция BuildCommDCB заполняет по собственному усмотрению (см. описание полей структуры DCB в разделе "Структура DCB"). Так, BuildCommDCB запрещает управление потоком данных между модемом и компьютером на аппаратном уровне (сигналы CTS и RTS) и на программном уровне (с помощью символов XON и XOFF).
Кроме того, функция BuildCommDCB позволяет определить только символы длиной 7 или 8 бит. Другие значения вызывают ошибку.
Таким образом, функция BuildCommDCB может быть полезна только в самых простых случаях или для начального заполнения структуры DCB. В большинстве случаев вам потребуется самостоятельно заполнять поля этой структуры.
Функция ClearCommBreak
Функция ClearCommBreak восстанавливает обмен данными через порт асинхронного последовательного адаптера, прерванный ранее вызовом функции SetCommBreak.
int ClearCommBreak(int idComDev);
Единственный параметр функции idComDev должен содержать идентификатор порта асинхронного последовательного адаптера, для которого вызывается функция.
Функция возвращает ноль в случае успешного завершения или -1, если параметр idComDev задан неправильно, то есть не соответствует ни одному открытому порту асинхронного последовательного адаптера.
Функция CloseComm
COM-порт является аппаратным ресурсом компьютера. Когда приложение открывает COM-порт, он становится недоступен для других приложений. Поэтому после использования порта, его следует освободить. Для этой цели нужно воспользоваться функцией CloseComm. Функция CloseComm имеет следующий прототип:
int CloseComm(int idComDev);
Единственный параметр idComDev должен содержать идентификатор порта асинхронного последовательного адаптера, который будет закрыт. Идентификатор порта асинхронного последовательного адаптера возвращает описанная выше функция OpenComm.
Функция возвращает ноль в случае успешного завершения или число, меньшее нуля, в случае ошибки. Одной из причин возникновения ошибки может быть неправильное значение параметра idComDev (т. е. если вы пытаетесь закрыть порт, который не был открыт.
Функция CloseComm закрывает определенный порт асинхронного последовательного адаптера и освобождает всю оперативную память, выделенную для входной и выходной очереди данных. Перед тем как порт будет закрыт, все символы из выходной очереди будут переданы в COM-порт. Чтобы закрыть порт, не дожидаясь передачи всех оставшихся в выходной очереди символов, можно воспользоваться функцией FlushComm, сбрасывающей содержимое очередей без передачи.
// Сбросить выходную очередь
FlushComm(nPortID,0);
// Сбросить входную очередь
FlushComm(nPortID,1);
// Закрыть COM-порт имеющий идентификатор nPortID
CloseComm(nPortID);
Функция EnableCommNotification
Функция EnableCommNotification разрешает или запрещает передачу сообщения WM_COMMNOTIFY в функцию окна приложения. Более подробную информацию о сообщении WM_COMMNOTIFY можно получить, прочитав раздел "Сообщение WM_COMMNOTIFY".
Прототип функции EnableCommNotification представлен ниже.
BOOL EnableCommNotification(int idComDev, HWND hwnd,
int cbWriteNotify, int cbOutQueue);
Параметр idComDev является идентификатором порта, который будет передавать сообщения WM_COMMNOTIFY в функцию окна. Для параметра idComDev необходимо использовать значение, полученное от функции OpenComm.
Параметр hwnd определяет окно, функции которого будет передаваться сообщение WM_COMMNOTIFY. Если этот параметр содержит значение NULL, функция EnableCommNotification запрещает передачу сообщения WM_COMMNOTIFY в функцию текущего окна.
Параметр cbWriteNotify определяет количество байт, которое драйвер асинхронного последовательного адаптера должен записать во входную очередь перед тем, как будет передано сообщение WM_COMMNOTIFY, у которого в младшем слове параметра lParam записан код извещения CN_RECEIVE . Данное сообщение служит сигналом приложению, что пора прочитать данные из входной очереди COM-порта.
Последний параметр cbOutQueue задает минимальное количество символов (байт) в очереди передатчика. Если в очереди передатчика становится меньше байт, чем определено параметром cbOutQueue, драйвер асинхронного последовательного адаптера передает приложению сообщение WM_COMMNOTIFY, у которого в младшем слове параметра lParam записан код извещения CN_TRANSMIT. Это сообщение означает, что выходная очередь практически пустая и можно поместить в нее очередную порцию данных.
В случае успешного завершения, функция возвращает значение отличное от нуля. Если функция завершилась с ошибками, она возвращает ноль. Причиной возникновения ошибки может послужить вызов функции EnableCommNotification с неправильным идентификатором COM-порта (COM-порт не открыт) или то, что драйвер асинхронного последовательного адаптера не поддерживает функцию EnableCommNotification.
Так, например, драйвер асинхронного последовательного адаптера COMM. DRV операционной системы Windows версии 3.0 не поддерживает эту функцию.
Вы можете запретить передачу функции окна сообщения WM_COMMNOTIFY с кодами извещения CN_RECEIVE и CN_TRANSMIT.
Если задать для параметра cbWriteNotify значение -1, драйвер не будет передавать функции окна сообщение WM_COMMNOTIFY с кодом извещения CN_RECEIVE.
Аналогично, если задать для параметра cbOutQueue значение -1, драйвер не будет передавать функции окна сообщение WM_COMMNOTIFY с кодом извещения CN_TRANSMIT.
Может возникнуть резонный вопрос: что произойдет, если разрешить генерацию сообщения WM_COMMNOTIFY с кодом извещения CN_RECEIVE, задать пороговое значение для передачи сообщения 20 байт, а из COM-порта поступит только 15 байт? Будет ли драйвер ожидать прихода остальных 5 байт и как долго? Не прекратится ли обмен данными?
Драйвер асинхронного последовательного адаптера периодически, с интервалом около 100 миллисекунд, опрашивает все очереди COM-портов. Если при очередном опросе входной очереди в ней будет находится меньше чем cbWriteNotify байт, функции окна все равно передается сообщение WM_COMMNOTIFY с кодом извещения CN_RECEIVE. Таким образом, если во входной очереди находится меньше байт, чем надо для генерации сообщения, функция окна все равно получит сообщение WM_COMMNOTIFY по истечении 100 миллисекунд.
Функция FlushComm
Функция FlushComm удаляет все символы из входной или выходной очереди COM-порта. Прототип функции имеет следующий вид:
int FlushComm(int idComDev, int fnQueue);
Параметр idComDev является идентификатором COM-порта, очередь которого необходимо очистить (сбросить).
Параметр fnQueue определяет очередь, из которой будут удалены все символы. Если значение этого параметра равно нулю, очищается выходная очередь. Если же значение параметра равно единице, удаляются все символы из приемной очереди.
Функция возвращает ноль в случае нормального завершения. Если значение, возвращаемое функцией, меньше нуля, параметр idComDev определяет неоткрытый COM-порт или параметр fnQueue задает несуществующую очередь. Возвращаемое функцией число принимает положительное значение, если возникла ошибка COM-порта. Подробный список возможных ошибок приведен в описании функции GetCommError.
Функция GetCommError
Функция GetCommError позволяет определить текущее состояние COM-порта и причину ошибки при предыдущем вызове коммуникационной функции.
Когда случается ошибка при передаче или приеме данных, Windows блокирует COM-порт до тех пор, пока не будет вызвана функция GetCommError.
int GetCommError(int idComDev, COMSTAT FAR* lpStat);
Параметр idComDev должен содержать идентификатор порта, который будет проверяться.
Через параметр lpStat передается дальний указатель на структуру типа COMSTAT. В поля этой структуры будет записано состояние COM-порта. В случае если вы вызовете GetCommError с параметром lpStat, равным NULL, функция вернет только значение ошибки.
Структура COMSTAT определена в файле WINDOWS.H следующим образом:
#if (defined(STRICT) (WINVER >= 0x030a))
// Основное определение структуры COMSTAT
typedef struct tagCOMSTAT
{
BYTE status;
UINT cbInQue;
UINT cbOutQue;
} COMSTAT;
// Определение вспомогательных констант
#define CSTF_CTSHOLD 0x01
#define CSTF_DSRHOLD 0x02
#define CSTF_RLSDHOLD 0x04
#define CSTF_XOFFHOLD 0x08
#define CSTF_XOFFSENT 0x10
#define CSTF_EOF 0x20
#define CSTF_TXIM 0x40
#else /* (STRICT WINVER >= 0x030a) */
// Альтернативное определение структуры COMSTAT
typedef struct tagCOMSTAT
{
BYTE fCtsHold :1;
BYTE fDsrHold :1;
BYTE fRlsdHold :1;
BYTE fXoffHold :1;
BYTE fXoffSent :1;
BYTE fEof :1;
BYTE fTxim :1;
UINT cbInQue;
UINT cbOutQue;
} COMSTAT;
#endif /* !(STRICT WINVER >= 0x030a */
Из приведенного выше листинга видно, что структура COMSTAT определяется по-разному в зависимости от следующего условия:
#if(defined(STRICT) (WINVER >= 0x030a))
// Основное определение структуры COMSTAT
// ....
#else /* (STRICT WINVER >= 0x030a) */
// Альтернативное определение структуры COMSTAT
// ....
#endif
Если вы создаете загрузочный модуль для операционной системы Windows 3.1 или устанавливаете жесткий режим контроля синтаксиса программы, определив константу STRICT, используется основное определение структуры COMSTAT. Опишем поля структуры COMSTAT при использовании основного определения:
Поле |
Описание |
status |
Определяет состояние передачи данных. Оно может содержать один или несколько флагов: CSTF_CTSHOLD Передача данных приостановлена в ожидании сигнала CTS CSTF_DSRHOLD Передача данных приостановлена в ожидании сигнала DSR CSTF_RLSDHOLD Передача данных приостановлена в ожидании сигнала RLSD CSTF_XOFFHOLD Передача данных приостановлена после приема символа XOFF CSTF_XOFFSENT Передача данных приостановлена при передаче символа XOFF. Передача приостанавливается, когда символ XOFF передан. CSTF_XOFFSENT используется системами, которые принимают следующий символ XON вне зависимости от настоящего принятого символа CSTF_EOF Получен символ конца файла EOF CSTF_TXIM Произошла задержка при передаче символа |
cbInQue |
Количество символов, находящихся во входной очереди |
cbOutQue |
Количество символов в выходной очереди |
Возвращаемое значение может быть комбинацией из следующих флагов:
Флаг |
Описание |
CE_BREAK |
Обнаружено состояние разрыва связи (BREAK) |
CE_CTSTO |
Тайм-аут CTS. Во время передачи символа сигнал CTS отсутствовал дольше промежутка времени, определенного полем fCtsHold структуры COMSTAT |
CE_DNS |
Параллельный порт не был выбран |
CE_DSRTO |
Тайм-аут DSR. Во время передачи символа сигнал DSR отсутствовал промежуток времени, определенный полем fDsrHold структуры COMSTAT |
CE_FRAME |
Обнаружена ошибка формата кадра (framing error) |
CE_IOE |
Во время попытки взаимодействия с параллельным портом произошла ошибка ввода/вывода |
CE_MODE |
Запрашиваемый режим не поддерживается или идентификатор COM-порта недействителен. Если установлен этот флаг, другие флаги следует игнорировать |
CE_OOP |
Параллельный адаптер передал компьютеру сигнал "out of paper" - конец бумаги |
CE_OVERRUN |
Символ не был прочитан из регистров COM-порта до прихода следующего символа. В результате этот символ был потерян |
CE_PTO |
Истекло время (тайм-аут) при выполнении попытки взаимодействия с параллельным устройством |
CE_RLSDTO |
Тайм-аут RLSD. Во время передачи символа сигнал RLSD отсутствовал промежуток времени, определенный полем fRlsdHold структуры COMSTAT |
CE_RXOVER |
Очередь приемника переполнена. Либо очередь приемника переполнена, либо символ был получен после получения символа конца файла |
CE_RXPARITY |
Обнаружена ошибка четности |
CE_TXFULL |
Очередь передатчика полностью заполнена, но функция пытается записать новые данные в эту очередь |
Функция GetCommEventMask
Функция GetCommEventMask получает и затем очищает слово события данного COM-порта.
UINT GetCommEventMask(int idComDev, int fnEvtClear);
Параметр idComDev является идентификатором COM-порта.
Параметр fnEvtClear определяет события, которые будут сброшены в слове событий.
Если функция завершается успешно, она возвращает значение слова событий для COM-порта, определенного параметром idComDev. Каждый бит этого слова отвечает за одно конкретное событие. Если данное событие произошло, соответствующий бит равен 1.
Перед тем как функция GetCommEventMask может зарегистрировать возникновение события, приложение должно разрешить данное событие при помощи функции SetCommEventMask.
При возникновении таких событий, как изменение состояния линии или ошибка принтера, вы должны вызвать функцию GetCommError.
Функция GetCommState
Функция GetCommState позволяет узнать режим работы порта асинхронного последовательного адаптера. Прототип функции представлен ниже:
int GetCommState(int idComDev, DCB FAR* lpdcb);
Параметр idComDev должен содержать идентификатор COM-порта. Это значение возвращает функция OpenComm.
Параметр lpdcb является дальним указателем на структуру DCB, в которую будет записана информация о режиме работы COM-порта, указанного в параметре idComDev.
В случае успешного завершения функция GetCommState возвращает нулевое значение. Если при работе функции возникла ошибка, возвращаемая величина меньше нуля.
Функция OpenComm
Перед тем как приложение сможет начать работу с портом асинхронного последовательного адаптера, оно должно открыть этот порт при помощи функции OpenComm.
int OpenComm(LPCSTR lpszDevControl,
UINT cbInQueue, UINT cbOutQueue);
Первый параметр функции lpszDevControl определяет открываемый порт и является указателем на строку, закрытую двоичным нулем, содержащую имя порта. Строка должна иметь формат "COMn" для асинхронного последовательного адаптера или "LPTn" для параллельного адаптера. Вместо символа n следует указать номер открываемого порта.
Как вы уже знаете из предыдущих разделов, COM-порт работает под управлением специального драйвера асинхронного последовательного адаптера. Драйвер принимает данные из порта и записывает их во входную очередь. Затем приложение, по мере надобности может прочитать данные из входной очереди. Когда приложение передает данные в COM-порт, они сначала попадают в выходную очередь драйвера, после чего драйвер передает их непосредственно асинхронному адаптеру.
При вызове функции OpenComm вы должны сами определить размер входной и выходной очереди драйвера асинхронного последовательного адаптера.
Параметр cbInQueue задает размер входной очереди COM-порта в байтах, а параметр cbOutQueue - размер выходной очереди в байтах.
В случае успешного выполнения функция возвращает число, определяющее открытый COM-порт. В дальнейшем оно будет использоваться практически всеми телекоммуникационными функциями. Мы будем называть его идентификатором COM-порта.
Если по какой-либо причине COM-порт не открыт, функция OpenComm возвращает отрицательное значение - код ошибки. В следующей таблице перечислены возможные значения, возвращаемые функцией в случае возникновения ошибки:
Значение | Смысл | ||
IE_BADID | Неверный или неподдерживаемый идентификатор COM-порта | ||
IE_BAUDRATE | Установлена скорость передачи информации не поддерживается | ||
IE_BYTESIZE | Ошибка при определении размера передаваемых и принимаемых данных | ||
IE_DEFAULT | Не поддерживаются характеристики порта, принятые по умолчанию | ||
IE_HARDWARE | Порт недоступен. Возможно, порт уже используется другим приложением | ||
IE_MEMORY | Не хватает оперативной памяти для размещения входной и выходной очередей COM-порта | ||
IE_NOPEN | Порт не открыт | ||
IE_OPEN | Порт уже открыт |
Функцию OpenComm можно использовать для того, чтобы узнать открыт ли данный COM-порт. Если перед вызовом функции OpenComm присвоить параметрам cbInQueue и cbOutQueue нулевые значения, то функция возвращает константу IE_OPEN, в случае, когда порт уже открыт или IE_MEMORY в противном случае.
В операционных системах Windows 3.1 и Windows for Worksgroups 3.11 можно использовать COM-порты с номерами от 1 до 9 (COM1-COM9) и параллельные порты от 1 до 3 (LPT1-LPT3). Если вы укажете номер порта, не поддерживаемый драйвером, функция OpenComm вернет код ошибки.
Сразу после открытия порта для него устанавливаются режим, принятый по умолчанию (скорость передачи информации, формат данных и т. д.). Чтобы изменить этот режим, необходимо воспользоваться функцией SetCommState.
Ниже мы приводим исходный текст функции OpenComPort, который вы можете использовать для открывания COM-порта. Чтобы открыть порт, достаточно передать этой функции номер порта.
//==========================================================
// Функция OpenCommPort
//==========================================================
int OpenCommPort(int nNumPort)
{
// Временный буфер для создания имени порта
char szTmpNamePort[10];
wsprintf(szTmpNamePort, "COM%d", nNumPort);
// Открываем COM-порт
return OpenComm(szTmpNamePort, 8192, 8192);
}
Функция OpenCommPort самостоятельно формирует строку с текстовым именем открываемого COM-порта. Строка формируется с помощью функции wsprintf во временном буфере szTmpNamePort.
Размер входной и выходной очереди COM-порта задается равным 8192 байтам, чего вполне достаточно для простых приложений, не поддерживающих протоколы обмена файлами. Затем определенная нами функция возвращает идентификатор открытого порта или отрицательное число в случае ошибки.
Функция ReadComm
Функция ReadComm позволяет прочитать данные из входной очереди COM-порта. Прототип функции представлен ниже:
int ReadComm(int idComDev, void FAR* lpvBuf, int cbRead);
Параметр idComDev является идентификатором COM-порта, из которого будут прочитаны данные.
Параметр lpvBuf содержит дальний указатель на буфер, в который будут записаны прочитанные из COM-порта данные.
Последний параметр cbRead задает количество символов, которое следует прочитать из входной очереди порта.
Следите за тем, чтобы значение cbRead не было больше, чем размер буфера lpvBuf
В случае успешного выполнения функции возвращаемое значение определяет количество символов, прочитанное из COM-порта. Если во время выполнения функции произошла ошибка, функция возвращает отрицательное значение. При этом абсолютное значение возвращенной величины определяет количество символов, успешно прочитанных из COM-порта.
В случае возникновения ошибки ее причину можно выяснить с помощью функции GetCommError. Так как ошибка может произойти и в том случае, когда ни один символ из входной очереди не прочитан, то, если ReadComm возвращает нулевую величину, следует вызвать функцию GetCommError, чтобы удостоверится в отсутствии ошибок.
Если величина, возвращаемая функцией меньше чем значение параметра cbRead, это означает, что на момент вызова функции во входной очереди COM-порта находилось символов меньше, чем определено параметром cbRead.
Если величина, возвращаемая функцией равна значению параметра cbRead, то возможно, что в очереди приемника еще находятся данные. Поэтому можно сразу повторно вызвать функцию ReadComm.
В качестве примера использования функции ReadComm мы приводим исходный текст функции ReadCommChar, считывающей из входной очереди один символ:
//==========================================================
// Функция ReadCommChar
//==========================================================
int ReadCommChar(int nPortID)
{
int iResult = 0;
int iErr = 0;
// Считываем из COM-порта один символ
iErr = ReadComm(nPortID, (LPSTR)&iResult, 1);
// Если символ не прочитан, обрабатываем ошибку
if(iErr != 1)
{
iResult = -1;
// Сбрасываем флаги ошибок
GetCommError(nPortID, NULL);
}
return iResult;
}
В качестве параметра для функции ReadCommChar необходимо передать идентификатор COM-порта. Функция ReadCommChar вызывает ReadComm и пытается считать из входной очереди COM-порта один символ.
Если ReadComm возвращает единицу, символ успешно прочитан и функция ReadCommChar возвращает код полученного символа. В противном случае для того, чтобы сбросить флаги ошибок, вызывается функция GetCommError. Затем функция ReadCommChar возвращает -1.
Функция SetCommBreak
Функция SetCommBreak прекращает передачу данных и переводит COM-порт в состояние BREAK. COM-порт находится в состоянии BREAK до тех пор, пока приложение не вызовет функцию ClearCommBreak.
int SetCommBreak(int idComDev);
Параметр idComDev должен содержать идентификатор COM-порта, переводимого в состояние BREAK. В качестве параметра idComDev необходимо использовать величину, возвращаемую функцией OpenComm.
При успешном завершении функция возвращает ноль. Если функция возвращает значение меньшее нуля, произошла ошибка.
Функция SetCommEventMask
Для каждого COM-порта Windows поддерживает слово событий. Оно состоит из набора бит, которые устанавливаются при изменении состояния COM-порта (например, при изменении состояния сигнала CTS). Чтобы при изменении состояния COM-порта соответствующим образом менялось слово состояния, необходимо сначала разрешить регистрацию этих изменений.
Функция SetCommEventMask разрешает регистрацию изменений состояния COM-порта в слове событий данного COM-порта:
UINT FAR* SetCommEventMask(int idComDev, UINT fuEvtMask);
Параметр idComDev является идентификатором COM-порта. Это значение возвращает функция OpenComm.
Параметр fuEvtMask определяет, какие события будут разрешены. Этот параметр может быть комбинацией из любых констант, перечисленных в следующей таблице:
Константа | Смысл | ||
EV_BREAK | Устанавливается при обнаружении состояния BREAK на входной линии | ||
EV_CTS | Устанавливается, когда сигнал CTS изменяет свое состояние | ||
EV_CTSS | Указывает текущее состояние сигнала CTS | ||
EV_DSR | Устанавливается, когда сигнал DSR изменяет свое состояние | ||
EV_ERR | Устанавливается при возникновении ошибки на линии. Такими ошибками являются CE_FRAME, CE_OVERRUN, и CE_RXPARITY (см. описание функции GetCommError) | ||
EV_PERR | Устанавливается при возникновении ошибки принтера для параллельного порта. Такими ошибками являются CE_DNS, CE_IOE, CE_LOOP и CE_PTO (см. описание функции GetCommError) | ||
EV_RING | Указывает текущее состояние сигнала RI во время последнего прерывания от модема | ||
EV_RLSD | Устанавливается, когда сигнал RLSD изменяет свое состояние | ||
EV_RLSDS | Указывает текущее состояние сигнала RLSD | ||
EV_RXCHAR | Устанавливается, когда символ принимается и помещается во входную очередь | ||
EV_RXFLAG | Устанавливается, когда определенный в структуре DCB символ принимается и помещается во входную очередь COM-порта | ||
EV_TXEMPTY | Устанавливается, когда последний символ из выходной очереди отправлен в COM-порт и выходная очередь становится пустой |
Функция SetCommEventMask возвращает указатель на слово событий данного COM-порта. Каждый бит этого слова отвечает за определенное событие. Если бит установлен, значит соответствующее событие произошло.
В слово событий записываются только события, разрешенные функцией SetCommEventMask.
Воспользовавшись функцией EnableCommNotification, можно разрешить передачу в функцию окна приложения сообщения WM_COMMNOTIFY с кодом извещения CN_EVENT. Это сообщение будет передаваться при изменении слова состояния COM-порта.
Функция SetCommState
Итак, мы научились открывать и закрывать COM-порт. Однако при вызове функции OpenComm мы указали только его имя и размеры входной и выходной очереди. Ни одной характеристики COM-порта - скорости передачи или формата данных мы не указывали.
Дело в том, что функция OpenComm устанавливает для открываемого порта скорость обмена, формат данных и другие характеристики, принятые по умолчанию. Чтобы изменить эти характеристики, необходимо воспользоваться функцией SetCommState.
Функция SetCommState устанавливает новый режим COM-порта, задаваемый полями структуры DCB, передаваемой ей в качестве одного из параметров.
int SetCommState(const DCB FAR* lpdcb);
Параметр lpdcb является дальним указателем на структуру DCB, содержащую описание устанавливаемого режима COM-порта. Подробное описание полей этой структуры мы привели чуть ниже.
Если функция SetCommState успешно установила новый режим работы COM-порта, она возвращает нулевое значение. В противном случае функция возвращает отрицательное значение.
Функция проводит повторную инициализацию всего аппаратного обеспечения COM-порта, но не сбрасывает входную и выходную очереди. Поэтому символы, расположенные во входной и выходной очередях, не будут удалены. В том случае, если желательно удалить эти символы и очистить очереди, следует воспользоваться функцией FlushComm.
Функция TransmitCommChar
Функция TransmitCommChar помещает символ, заданный параметром chTransmit, в начало выходной очереди COM-порта, определенного параметром idComDev. Прототип функции представлен ниже.
int TransmitCommChar(int idComDev, char chTransmit);
Функция возвращает нулевое значение в случае успешного завершения или отрицательное значение, если символ не записан в выходную очередь. Функцию нельзя вызывать повторно, если COM-порт не производит передачу данных. После того как функция TransmitCommChar записала символ в начало выходной очереди, необходимо, чтобы перед повторным вызовом функции этот символ был передан.
Если предыдущий символ не был передан, перед повторным вызовом функции TransmitCommChar, функция завершается с ошибкой.
В следующем примере функция TransmitCommChar используется для передачи символов, набранных на клавиатуре в COM-порт.
case WM_CHAR:
ch = (char)wParam;
// Помещаем код нажатой клавиши в начало выходной очереди
TransmitCommChar(idComDev, ch);
// Добавляем символ перевода строки LF
// для каждого символа возврата каретки
if (ch == 0x0d)
TransmitCommChar(idComDev, 0x0a);
return TRUE;
Функция UngetCommChar
Функция позволяет поместить символ в начало входной очереди. При выполнении последующей операции чтения функция ReadComm прочитает этот символ. Прототип функции UngetCommChar представлен ниже:
int UngetCommChar(int idComDev, char chUnget);
Первый параметр функции idComDev должен содержать идентификатор COM-порта.
Параметр chUnget должен содержать символ, помещаемый во входную очередь COM-порта.
Функция возвращает нулевое значение в случае успешного завершения или отрицательное значение при возникновении ошибки.
Последовательные вызовы функции UngetCommChar не допускаются. Перед тем как вы сможете вызвать функцию UngetCommChar еще раз, символ, помещенный в очередь приемника, должен быть прочитан.
Функция WriteComm
Функция WriteComm записывает символы в выходную очередь COM-порта. Прототип функции представлен ниже:
int WriteComm( int idComDev, const void FAR* lpvBuf,
int cbWrite);
Параметр idComDev содержит идентификатор COM-порта, используемого для передачи данных.
Параметр lpvBuf является дальним указателем на буфер, содержащий передаваемые данные.
Последний параметр функции cbWrite определяет количество символов в буфере lpvBuf, предназначенных для передачи.
Обратите внимание на то, что значение параметра cbWrite не должно быть больше, чем размер буфера lpvBuf
В случае успешного выполнения функции возвращаемое ей значение определяет количество символов, записанное в выходную очередь COM-порта. Если во время выполнения функции произошла ошибка, функция возвращает отрицательное значение. Абсолютное значение возвращаемой величины определяет количество символов, успешно записанных в выходную очередь.
В случае возникновения ошибки ее причину можно выяснить с помощью функции GetCommError.
Если в очереди передатчика недостаточно места, функция WriteComm может удалить данные из этой очереди. Поэтому перед вызовом функции WriteComm приложение должно проверить наличие достаточного свободного пространства в очереди передатчика. Для этого следует воспользоваться функцией GetCommError.
При открытии COM-порта функцией OpenComm следует указать размер выходной очереди не меньше, чем максимальный предполагаемый размер передаваемых строк.
В качестве примера использования функции WriteComm мы приводим исходный текст функции WriteCommChar, записывающей в выходную очередь один символ:
//==========================================================
// Функция WriteCommChar
//==========================================================
BOOL WriteCommChar(int nPortID, int nChar)
{
int iErr = 0;
iErr = WriteComm(nPortID, (LPSTR)&nChar, 1))
if(iErr != 1)
{
GetCommError(nPortID, NULL);
return FALSE;
}
return TRUE;
}
В качестве первого параметра функции WriteCommChar передается идентификатор COM-порта, в который необходимо передать символ. Второй параметр должен содержать код передаваемого символа.
Функция WriteCommChar вызывает функцию WriteComm и пытается записать в выходную очередь COM-порта один символ.
Если WriteComm возвращает единицу, значит символ успешно записан и функция WriteCommChar возвращает значение TRUE. В противном случае для того чтобы сбросить флаги ошибок вызывается функция GetCommError и WriteCommChar возвращает FALSE.
Инициализация асинхронного адаптера
Первое, что должна сделать программа, работающая с асинхронным адаптером, - установить формат и скорость передачи данных. После загрузки операционной системы для асинхронных адаптеров устанавливается скорость 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 <stdio.h>
#include <conio.h>
#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 <stdio.h>
#include <conio.h>
#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);
}
Инициализация COM-порта
Проводим инициализацию COM-порта, к которому подключен модем. Для этого программируем регистры микросхемы UART, задавая формат данных (число стоповых битов, длину слова) и скорость обмена. Чем скорость выше, тем, естественно, быстрее будет происходить обмен с удаленным модемом.
Инициализация FOSSIL-драйвера (COM-порта)
Данная функция используется для инициализации FOSSIL-драйвера. Эта функция должна быть вызвана перед вызовом других функций драйвера.
На входе: AH = 04h;
DX = номер порта: 0 - COM1, 1 - COM2, 2 - COM3,
3 - COM4 и т. д.;
если BX = 4F50h
ES:CX - указатель на флаг <Ctrl-C>.
На выходе: AX = 1954h;
BL = максимальный номер функции (регистр AH при
вызове прерывания INT 14h), поддерживаемой
данным драйвером, не считая функций с
номерами, большими 7Dh;
BH = уровень драйвера.
Если при вызове данной функции регистр BX равен 4F50h, то регистры ES:CX указывают на однобайтный счетчик, содержимое которого увеличивается при нажатии комбинации клавиш <Ctrl-C>.
При инициализации драйвера происходит установка сигнала DTR. Для сброса драйвера (очистки буферов, сброса флага управления потока и т. д.) необходимо вызвать эту функцию второй раз. Данную функцию можно использовать для проверки, установлен ли FOSSIL-драйвер.
Инициализация модема
Передавая модему AT-команды через COM-порт, выполняем его инициализацию. При помощи AT-команд можно установить различные режимы работы модема - выбрать протокол обмена (CCITT или Bell), установить набор диагностических сообщений модема и т.д.
Использование FOSSIL-драйверов
Название FOSSIL является набором первых символов из названий нескольких коммуникационных программ - "Fido/Opus/SEAdog Standard Layer". Эти программы используют FOSSIL-драйверы для работы с асинхронным последовательным адаптером.
FOSSIL-драйверы используются для расширения функций BIOS, обслуживающих асинхронный последовательный адаптер и модем. Кроме того, FOSSIL-драйверы поддерживают несколько функций для работы с клавиатурой, видеоадаптером и системным таймером.
Использование FOSSIL-драйверов позволяет увеличить скорость обмена через последовательный адаптер до 38400 бит/с (функции BIOS допускают максимальную скорость только 9600 бит/с).
FOSSIL-драйвер самостоятельно обрабатывает прерывания от COM-портов. Он содержит два внутренних буфера, организованных в виде очереди.
В первый буфер - буфер передатчика - записываются данные, передаваемые компьютером модему. Драйвер самостоятельно определяет, когда асинхронный адаптер способен передать внешнему устройству очередной символ (т. е. когда свободен регистр данных COM-порта) и записывает его в регистр данных COM-порта. При этом переданный символ удаляется из буфера и происходит передача следующего символа.
Во второй буфер - буфер приемника - драйвер записывает данные, поступающие в компьютер через COM-порт. Затем содержимое этого буфера может быть считано программой при помощи специальной функции драйвера.
Примером такого FOSSIL-драйвера может служить драйвер Gwinn's Communications Controller, X00.SYS Version V1.30. Вы можете получить любые FOSSIL-драйверы и документацию на них практически на каждой станции BBS.
Существуют специальные FOSSIL-драйверы, обеспечивающие программную эмуляцию аппаратных протоколов коррекции ошибок - от MNP2 до MNP5. Дополнительные функции, поддерживаемые этими драйверами, мы рассмотрим позже.
Использование прерываний COM-порта
Если ваша коммуникационная программа будет использовать прерывания, можно организовать буфер принимаемых и передаваемых данных. Обработчик прерываний должен проанализировать причину прерывания и либо передать в COM-порт очередной символ из буфера передатчика (если прерывание произошло в результате передачи очередного символа), либо считать поступивший символ из регистра данных и записать его в буфер приемника (если прерывание произошло в результате приема от модема очередного символа).
В этом случае процесс обмена идет в фоновом режиме и процессор может спокойно заниматься обработкой принимаемых и передаваемых символов. Если программе понадобится передать данные модему, она может просто записать их в буфер передатчика. Для приема данных она должна считать их из буфера приемника.
При работе с асинхронным последовательным адаптером (COM-портом) вы можете использовать механизм прерываний. Глава "Программирование асинхронного адаптера" содержит теоретические сведения по этому вопросу, а в главе "Коммуникационная программа, использующая прерывания" содержится исходный текст коммуникационной программы, использующей прерывания для работы с COM-портом. Если ваша программа использует прерывания от COM-порта, она должна содержать обработчик прерываний, а также программировать контроллер прерываний для разрешения прерываний.
Так как передача и прием данных модемом представляют собой длительный процесс, то применение прерываний от COM-порта позволяет использовать процессорное время для других нужд.
Изменения в регистрах 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 |
Изменилось состояние модема
Прерывание происходит при изменении состояния входных линий CTS, RI, DCD, DSR. Состояние этих линий можно определить, считав регистр состояния модема. Это прерывание используется для обнаружения звонка на телефонной линии. Прерывание автоматически сбрасывается после чтения регистра состояния модема.
Как использовать буферизацию?
Обычно без использования буферизации 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.
Как определить тип микросхемы UART
Как же определить, какая из этих микросхем UART установлена на вашем асинхронном адаптере? Кроме возможности заглянуть в документацию, существует еще один способ. Фактически этот способ основан на различиях в особенностях микросхем UART. Ниже приведены особенности микросхем UART различных типов:
UART8250 не имеет регистра расширения
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.