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

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

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

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

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

К сожалению, библиотеки трансляторов 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) ? "Да" : "Нет" );
   }
}

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