Программирование модемов

Стандартные функции библиотеки Си для работы с последовательным портом


К сожалению, библиотеки трансляторов Microsoft Quick C 2.5 и C 6.0, а также трансляторов Borland C++ и Turbo C содержат всего одну функцию управления последовательным портом ввода/вывода. Трансляторы Microsoft Quick C 2.5 и C 6.0 содержат функцию _bios_serialcom(), а Borland C++ и Turbo C - функцию bioscom().

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

Заметим, что функции _bios_serialcom() и bioscom() работают только на компьютерах, полностью совместимых с IBM PC/XT/AT.

При использовании функций _bios_serialcom() и bioscom() необходимо включить директивой #include файл bios.h. Для трансляторов фирмы Borland этот файл включает объявление функции bioscom(), а для Microsoft кроме объявления функции _bios_serialcom() - также определения констант, которые можно использовать с этой функцией.

Рассмотрим функцию _bios_serialcom():

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() сначала инициализирует последовательный порт, а затем передает символы, набранные на клавиатуре в порт, а символы, считанные из порта, - на экран компьютера.

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

// QC_LIB.C // программа иллюстрирует доступ к последовательному порту // через функцию _bios_serialcom()

#include <bios.h> // необходимо включить при // использовании _bios_serialcom() #include <stdio.h>

#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:

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

Эта функция аналогична функции _bios_serialcom(), трансляторов Microsoft Quick C 2.5 и C 6.0, за исключением следующих моментов:


  • отличается порядок следования аргументов функции;
  • не соответствуют типы аргументов, имеющие одинаковый смысл;
  • для трансляторов Borland C++ и Turbo C во включаемом файле bios.h отсутствует определение констант _COM_xxx.




Рассмотрим подробнее аргументы функции 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)определить состояние порта
Так как _COM_xxx константы не определены, то для совместимости с трансляторами Microsoft и для удобства мы можем определить их самостоятельно:

// BC_CONST.H // определение констант для Turbo C и Borland C++

#define _COM_INIT 0 #define _COM_SEND 1 #define _COM_RECEIVE 2 #define _COM_STATUS 3

#define _COM_CHR7 0x02 #define _COM_CHR8 0x03 #define _COM_STOP1 0x00 #define _COM_STOP2 0x04



#define _COM_NOPARITY 0x00 #define _COM_EVENPARITY 0x18 #define _COM_ODDPARITY 0x08

#define _COM_110 0x00 #define _COM_150 0x20 #define _COM_300 0x40 #define _COM_600 0x60 #define _COM_1200 0x80 #define _COM_2400 0xa0 #define _COM_4800 0xc0 #define _COM_9600 0xe0

Аналогично функции _bios_serialcom() функция bioscom() возвращает 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 изменила состояние
Приведем небольшой пример использования функции bioscom(). Данная программа проверяет состояние линий DSR и CTS для асинхронных портов COM1..COM4. Если обе линии DSR и CTS находятся в активном состоянии, значит устройство, подключенное к данному порту готово к работе (активно).

// BC_LIB.C // программа иллюстрирует доступ к последовательному порту // через функцию bioscom()

#include <bios.h> //необходимо включить при использовании _bios_serialcom() #include <stdio.h>

#include "bc_const.h" // определяем констаны _COM_xxx

void main(void) {

unsigned status, port;

for( port = 0; port < 4; port++ ) {

status = bioscom( _COM_STATUS, 0, port, );

// Проверяем состояние каждого последовательного порта // и определяем наличие присоединенных к нему устройств // типа модема. // Считаем, что если биты data-set-ready и clear-to-send // установлены в единицу, то внешнее устройство отвечает.

printf( "COM%c состояние: %.4X\tАктивный: %s\n", (char)port + '1', status, (status & 0x0030) ? "Да" : "Нет" ); } }


Содержание раздела