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

Аппаратное обеспечение персонального компьютера

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

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

Программирование асинхронного адаптера

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

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

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

Учитывая все это, для программирования асинхронного адаптера мы рекомендуем использовать порты ввода/вывода микросхемы UART.

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

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

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

Если вам надо задать новое значение скорости обмена данными, перед записью байта режима установите старший бит этого байта. Затем последовательно двумя командами вывода загрузите делитель частоты тактового генератора. Младший байт запишите в порт 3F8h, старший - в порт 3F9h.

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

На этом инициализацию можно считать законченной.

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

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

Признаком того, что регистр передатчика свободен, является установленный бит 5 регистра состояния линии с адресом 3FDh.

Прием данных

Аналогично тому как это делается при передаче данных, перед вводом символа из порта приемника 3F8h необходимо убедиться в том, что бит 0 порта 3FDh установлен. Это означает, что символ принят из линии и находится в буферном регистре приемника.

Программа COMTEST

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

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

Листинг 6.1. Файл comtest\comtest.с


	// =====================================================
	// Работа с асинхронным адаптером COM1.
	// Перед запуском программы необходимо замкнуть
	// контакты 2 и 3 разъема COM1
	//
	// (C) Фролов А.В, 1997
	//
	// E-mail: frolov@glas.apc.org
	// WWW:    http://www.glasnet.ru/~frolov
	//            или
	//         http://www.dials.ccas.ru/frolov
	// =====================================================
	
	#include <stdio.h>
	#include <conio.h>
	
	typedef struct _AUX_MODE_ 
	{
	  union 
	  {
	    struct 
	    {
	      unsigned char len : 2, // длина символа
	           stop         : 1, // число стоп-битов
	           parity       : 2, // контроль четности
	           stuck_parity : 1, // фиксация четности
	           en_break_ctl : 1, // установка перерыва
	           dlab         : 1; // загрузка регистра делителя
	    } ctl_word;
	    char ctl;
	  } ctl_aux;
	  unsigned long baud; // скорость передачи данных
	} AUX_MODE;
	
	void aux_stat(AUX_MODE *mode, int port);
	int aux_init(AUX_MODE *mode, int port, int imask);
	void aux_outp(char chr, int port);
	char aux_inp(int port);
	
	int main(void) 
	{
	  AUX_MODE amd;
	
	  aux_stat(&amd, 0);
	  printf("\nСостояние порта COM1:"
	    "\nКод длины символа:    %d"
	    "\nКод числа стоп-битов: %d"
	    "\nКонтроль четности:    %d"
	    "\nСкорость передачи:    %lu",
	    amd.ctl_aux.ctl_word.len,
	    amd.ctl_aux.ctl_word.stop,
	    amd.ctl_aux.ctl_word.parity,
	    (unsigned long)amd.baud);
	
	  amd.baud = 115200;
	  aux_init(&amd, 0, 0);
	
	  aux_stat(&amd, 0);
	  printf("\nСостояние порта COM1:"
	    "\nКод длины символа:    %d"
	    "\nКод числа стоп-битов: %d"
	    "\nКонтроль четности:    %d"
	    "\nСкорость передачи:    %lu",
	    amd.ctl_aux.ctl_word.len,
	    amd.ctl_aux.ctl_word.stop,
	    amd.ctl_aux.ctl_word.parity,
	    (unsigned long)amd.baud);
	
	  printf("\n\nТестирование асинхронного адаптера."
	    "\nНажимайте клавиши!"
	    "\nДля завершения работы нажмите <Contril+C>\n");
	
	  for(;;) 
	  {
	    // Вводим символ с клавиатуры и передаем его
	    // в асинхронный адаптер
	    aux_outp((char)getch(), 0);
	
	    // Вводим символ из асинхронного адаптера и
	    // отображаем его на экране
	    putchar(aux_inp(0));
	  }
	  return 0;
	}
	
	/**
	*.Name         aux_stat
	*.Title        Определение режима асинхронного адаптера
	*
	*.Descr        Эта функция считывает текущий режим
	*              асинхронного порта и записывает его
	*              в структуру с типом AUX_MODE
	*
	*.Proto        void aux_stat(AUX_MODE *mode, int port);
	*
	*.Params       AUX_MODE mode - структура, описывающая
	*              протокол и режим работы порта:
	*
	*              int port - номер асинхронного адаптера:
	*                 0 - COM1, 1 - COM2
	**/
	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);
	}
	
	/**
	*.Name         aux_init
	*.Title        Инициализация асинхронного адаптера
	*
	*.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 - ошибки в параметрах инициализации.
	**/
	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;
	}
	
	/**
	*.Name         aux_outp
	*.Title        Вывод символа в асинхронный адаптер
	*
	*.Descr        Эта функция дожидается готовности
	*              передатчика и посылает символ
	*
	*.Proto        void aux_outp(char chr, int port);
	*
	*.Params       char chr - посылаемый символ;
	*
	*              int port - номер асинхронного адаптера:
	*                 0 - COM1, 1 - COM2
	**/
	void aux_outp(char chr, int port) 
	{
	  unsigned status_reg, out_reg;
	
	  status_reg = 0x3fd - 0x100 * port;
	  out_reg = status_reg - 5;
	
	  while( (inp(status_reg) & 0x20) == 0 );
	  outp(out_reg, chr);
	}
	
	/**
	*.Name         aux_inp
	*.Title        Ввод символа из асинхронного адаптера
	*
	*.Descr        Эта функция дожидается готовности
	*              приемника и вводит символ из асинхронного
	*              адаптера
	*
	*.Proto        char aux_inp(int port);
	*
	*.Params       int port - номер асинхронного адаптера:
	*                 0 - COM1, 1 - COM2
	*
	*.Return       Принятый символ
	**/
	char aux_inp(int port) 
	{
	  unsigned status_reg, inp_reg;
	
	  status_reg = 0x3fd - 0x100 * port;
	  inp_reg = status_reg - 5;
	
	  while( (inp(status_reg) & 1) == 0 );
	  return(inp(inp_reg));
	}

Использование прерываний

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

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

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

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


	mov al, 20h
	out 20h, al
	iret

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

Более подробное описание способов работы с последовательным асинхронным адаптером с применением прерываний и соответствующие примеры программ вы найдете в 16 томе «Библиотеки системного программиста», который называется «Модемы и факс-модемы».

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