Лабораторная работа № 2 Организация связи компьютера с внешними устройствами посредством последовательного интерфейса 1. Цели и задачи работы Изучение организации порта последовательной передачи данных (COM-порта) и приобретение практических навыков его использования для ввода и вывода информации. 2. Теоретические сведения Последовательный порт или COM-порт (от англ. communication) — двунаправленный последовательный интерфейс, предназначенный для обмена байтовой информацией. Порт называется последовательным потому, что информация через него передаётся по одной линии интерфейса бит за битом (в отличие от параллельного порта). Наиболее часто для последовательного порта персональных компьютеров используется стандарт RS-232C. Ранее последовательный порт использовался для подключения терминала, позже для модема или мыши. Сейчас он используется для соединения с источниками бесперебойного питания, для связи с аппаратными средствами разработки встраиваемых вычислительных систем. Хотя некоторые другие интерфейсы компьютера — такие как Ethernet, FireWire и USB — также используют последовательный способ обмена, название «последовательный порт» закрепилось за портом, имеющим стандарт RS-232C. Наиболее часто для реализации COM-интерфейса используются Д-образные разъёмы: 9- и 25-контактные, (DB-9 и DB-25 соответственно). Раньше использовались также DB-31 и круглые восьмиконтактные DIN-8. Максимальная скорость передачи обычно составляет 115200 бод. Контакты разъемов и обозначение линий интерфейса приведены на рисунке:
Приведем более подробное описание сигналов последовательного порта DB-9: CD – установленный сигнал показывает, что модемом или другим устройством обнаружена несущая частота. Сигнал CD# является сигналом состояния модема, и центральный процессор может определить его состояние путем чтения бита DCD из регистра MSR для соответствующего последовательного порта. Бит DCD в регистре MSR показывает, изменился ли сигнал CD# со времени последнего чтения регистра MSR. RXD – последовательные входы данных. TXD – при обычных условиях последовательные выходы данных. Во время аппаратной конфигурации (сигнал RSTDRV установлен и некоторое время спустя) эти выводы работают только как входы. DTR – Установленный сигнал показывает модему или другому устройству, что модуль последовательного порта готов установить соединение. Сигнал DTR# может быть установлен через регистр управления модемом (Modem Control Register (MCR)). Сигнал reset сбрасывает этот сигнал. Во время аппаратной конфигурации (сигнал RSTDRV установлен и некоторое время спустя) эти выводы работают только как входы. GND – общая земля. DSR – установленный сигнал показывает, что модем или другое устройство готовы произвести соединение с модулем последовательного порта. Сигнал DSR# является сигналом состояния модема, и центральный процессор может определить его состояние путем чтения бита DSR из регистра MSR для соответствующего последовательного канала. Бит DDSR в регистре MSR показывает, изменился ли сигнал CD# со времени последнего чтения регистра MSR. RTS – установленный сигнал информирует модем или другое устройство о том, что модуль последовательного порта готов к обмену данными. Сигнал RTS# может быть установлен через бит RTS регистра MCR. Сигнал reset сбрасывает этот бит. Во время аппаратной конфигурации (сигнал RSTDRV установлен и некоторое время спустя) эти выводы работают только как входы. CTS – установленный сигнал показывает, что модем или другое устройство готовы к обмену данными. Сигнал CTS# является входом состояния модема, и центральный процессор может определить его состояние путем чтения бита CTS из MSR соответствующего последовательного порта. Бит DCTS в MSR показывает, изменился ли сигнал CTS# со времени последнего чтения MSR. RI – установленный сигнал показывает что модемом или другим устройством принят телефонный звонок. Сигнал RI# является сигналом состояния модема, и центральный процессор может определить его состояние путем чтения бита RI из регистра MSR для соответствующего последовательного канала. Бит TERI в регистре MSR показывает, менялся ли уровень сигнала RI# с низкого на высокий со времени последнего чтения регистра MSR. Примечание: здесь MSR – регистр статуса модема (ModemStatusRegister) COM-порты в операционной системе типа Windows это именованные каналы для передачи данных, называемые обычно COM1, COM2 и т.д. по порядку обнаружения драйверов соответствующих устройств. Например, для обмена информации через Bluetooth многие драйверы представляются операционной системе как COM-порт, и резервируют похожее имя. Последовательный интерфейс RS-232C был разработан еще в 1969 г. и введен ассоциацией электронной промышленности (ElectronicIndustriesAssociations — EIA) как стандарт для связи аппаратных средств передачи данных (DataCommunicationEquipment — DCE), в качестве которых, как правило, используются компьютеры, с аппаратными средствами приема данных (DataTerminalEquipment — DTE) — периферийными устройствами (принтерами, модемами и т. п.). Из всего многообразия периферийных устройств, подключаемых к компьютеру, в настоящее время интерфейс RS-232 остался, пожалуй, лишь у выносных модемов, поскольку возросший на несколько порядков объем информации, передаваемой в периферийные устройства, потребовал новых высокоскоростных интерфейсов, таких, например, как параллельный (Centronics) или высокоскоростной последовательный (USB). Но сейчас подавляющее большинство модемов выпускаются в виде плат, которые вставляются в материнскую плату компьютера (и сопрягаются с ней по внутренним интерфейсам ISA или PCI) либо даже интегрируются в материнскую плату. Казалось бы, использованию интерфейса RS-232 приходит конец. Но нет, интерфейс RS-232 до сих пор имеет подавляющее большинство компьютеров, поскольку он и сейчас не утратил своего коммуникационного назначения, несмотря на низкую скорость обмена информацией. В чем причина такой поразительной живучести этого интерфейса? Второе рождение интерфейс RS-232 пережил около 10 лет назад, когда начался выпуск микроконтроллеров. Поначалу скорость работы микроконтроллеров была такой низкой, что единственным интерфейсом, который позволял производить обмен информацией между компьютером и микроконтроллером, был интерфейс RS-232. Но к настоящему времени скорость работы современных микроконтроллеров возросла как минимум на порядок, а интерфейс RS-232 все равно имеет практически любой микроконтроллер, причем многие микроконтроллеры оборудованы даже двумя такими интерфейсами. Причины, по которым интерфейс RS-232 продолжает до сих пор использоваться, по-видимому, состоят в том, что этот интерфейс достаточно надежен, допускает связь на расстоянии до 15 м (при скорости обмена 115200 бод) и более (при соответствующем снижении скорости), легко программируется (как в компьютере, так и в микроконтроллере). Всех этих свойств лишен, например, интерфейс USB, который сейчас присутствует практически во всех компьютерах. Передача данных через СОМ-порт осуществляется побайтно с последовательной передачей каждого бита по одной линии связи. Синхронизация процесса передачи данных требует поддержания заданного значения временного интервала, соответствующего одному биту; длительность временных интервалов между байтами произвольная. Асинхронный режим передачи байтов реализован на основе обрамления каждого байта специальными стартовыми и стоповыми битами. При отсутствии передаваемых битов на пинии удерживается уровень логической «1». Передача байтов данных начинается с посылки стартового бита с уровнем «0», после чего, начиная с младшего (нулевого) выдаются последовательно все биты байта. Затем может следовать бит четности, который обеспечивает четное или нечетное общее количество единиц в пакете битов и используется для обнаружений ошибок. Передача байта завершается одним или двумя стоповыми битами с уровнем «1». Эти стоп-биты начинают отмеченное состояние (нет передачи), которое будет сохраняться до тех пор, пока не начнется передача следующего байта данных. Число стоп-битов существенно, поскольку они устанавливают минимальное время, которое должно пройти перед следующим стартовым битом. Сказанное иллюстрирует следующий рисунок: Примечание: тактовый сигал при асинхронной передаче данных – это внутренний сигнал интерфейса. Бит четности, если он присутствует в передаваемом сообщении, используется для контроля коppектности передачи и поиска ошибок. Контроль передачи может проводиться как на четность (контрольный разряд равен сумме по модулю 2 информационных pазpядов и общее число единичных pазpядов четно), так и на нечетность (контрольный pазpяд не равен сумме по модулю 2 информационных pазpядов и общее число единичных pазpядов нечетно). Скорость передачи битов по каналу измеряется в бодах (изменение числа состояний линии в секунду). Наименьшей скоростью передачи информации считается 300 бод. Эта скорость передачи использовалась в старых модемах (сейчас большинство модемов позволяют достигать скорости передачи от 1200 до 56 000 бод). Семейство компьютеров IBM PC поддерживают скорость передачи данных в 56000 бод. Некоторые типы интерфейсов позволяют достигать скорости передачи данных в 1000000 бод! Синхронизация работы приемное и передатчика данных предполагает программирование их на работу с одинаковой скоростью передачи данных, на использование конкретного числа стартовых а стоповых битов и режима контроля. При программировании COM-портов на низком уровне используются десять регистров. Изменение состояния конкретного регистра производится посылкой байта данных в соответствующий порт. Рассмотрим основные из них: Порт 3F8h.Этот порт соответствует регистру передаваемых данных. Для передачи информации по интерфейсу в порт 3F8h необходимо записать байт данных. После приема данных от внешнего устройства они могут быть прочитаны из этого же порта. В зависимости от состояния бита управляющего слова, выводимого в управляющий регистр с адресом 3FBh, назначение порта 3F8h может изменяться. Если этот бит равен «0», порт используется для записи передаваемых даны. Если же этот бит равен «1», порт используется для вывода значения младшего байта делителя частоты тактового генератора. Изменяя содержимое делителя, можно изменять скорость передачи данных. Старший байт делителя записывается в порт 3F9h. Зависимость скорости передачи данных от значения делителя частоты приведены в таблице:
Порт 3F9h.Порт используется как регистр управления прерываниями от асинхронного адаптера или (после вывода в порт 3F9h байта с установленным в «1» старшим битом) для вывода значения старшего байта делителя частоты тактового генератора. В режиме регистра управления прерываниями порт имеет следующий формат.
Порт 3FAh.Регистр идентификации прерывания. По его содержимому программа может определить причину прерывания. Формат регистра приведён в таблице:
Порт 3FBh.Управляющий регистр, доступен по записи и чтению. Его формат показан в таблице:
Порт 3FCh.Регистр управления модемом. Управляет состоянием выходных линий DTR, RTS, линий, специфических для модемов OUT1 и OUT2, для запуска диагностики при входе асинхронного адаптера, замкнутым на его выход. Формат порта приведён в таблице:
Порт 3FDh.Регистр состояния линии. Значение зарядов регистра приведены в таблице:
Порт 3FEh.Регистр состояния модема. Значения битов указаны в таблице:
Перед записью байта данных в регистр передатчика нужно убедиться, что регистр хранения передатчика свободен, то есть убедиться в том, что передача предыдущего символа завершена. Признаком свободы регистра передатчика является установленный в «1» бит 5 регистра состояния линии с адресом 3FDh. Аналогично передачи данных перед вводом символа из порта приёмника 3F8h следует убедиться, что бит «0» порта 3FDh установлен в «1», то есть что символ принят из линии и находиться в буферном регистре приёмника. Программирование интерфейса RS-232 в операционных системах Windows возможно двумя способами. Первый, рассмотренный выше, – использование прямых команд ввода/вывода в порт. Второй – использование функций WINAPI (ApplicationProgramInterface) - программный интерфейс для разработки приложений. Использование функций API — стандартный путь, на который указывают все документированные источники по программированию в Windows. Однако, применение API-функций наталкивается на ряд проблем. В частности, эти функции работают очень медленно, и, если речь идет о быстрой реакции на какое-либо кратковременное (например, несколько микросекунд) внешнее воздействие на порт RS-232, то функции API не успевают отследить такое воздействие. Кроме того, некоторые функции работают с явными ошибками, т. е. либо выполняют не совсем те действия, которые, указаны в документации на конкретную функцию API, либо выполняют дополнительные действия, которые не описаны. Использование прямых команд ввода/вывода в порт тоже имеет свои проблемы. Однако программирование интерфейса прямыми командами ввода/вывода в порт хорошо известно, поскольку использовалось еще в DOS. Кроме того, прямые команды ввода/вывода в порт выполняются очень быстро – это машинные команды процессора. Они позволяют отследить самые быстротекущие процессы. Рассмотрим кратко те функции API, которые имеют непосредственно отношение к программированию интерфейса RS-232 в 32-разрядном режиме. Полное описание всех функций API можно найти в MSDN. Первой функцией API, которой должно предваряться программирование RS-232, является функция открытияпорта — CreateFileA (создать файл). Она возвращает "handle" порта - числовое значение, которое операционная система приписывает открытому порту. Дословно "handle" — ручка (потянув за которую можно «открыть» порт). Функция CreateFileA работает правильно и претензий не вызывает. После открытия порта необходимо обратиться к функции построения контрольного блока COM-порта: BuildCommDCBA, в которой требуется перечислить параметры порта (скорость обмена, формат данных, количество стоп-бит и т. п.). Эта функция включает в себя два параметра:ControlString и PortStruct. ControlString — строка символов, которая, например, может принимать следующее значение: ControlString='Com1: 115200,N,8,1’, означающее, что будет открыт порт СОМ1 (который будет работать на скорости 115 200 бод), бит паритета не используется, количество бит данных — 8 и стоп-бит—1. Параметр PortStruct — это массив байт, который определяет более полно структуру роботы COM-портаи в который, в частности, входят параметры из ControlString (скорость, бит паритета, количество бит данных и стоп-бит). Так вот, если, например, указать в ControlString скорость 115 200, а в PortStruct скорость 9600, то порт будет работать на скорости 9600 бод. Если же в ControlString указать 9600, а в PortStruct 115 200, то скорость работы порта может принять непредсказуемое значение. По этой причине изменять в PortStruct параметры, установленные в ControlString, не рекомендуется. Необходимо отметить, что функция BuildCommDCBA не производит никаких действий с портом, в ней только перечисляется, какие параметры порта необходимо установить, но установку этих параметров функция не производит. Для установки параметров, указанных в функции BuildCommDCBA в СОМ-порт используется функция SetCommState(установить статус порта). Эта функция претензий не вызывает и работает правильно. Для получения параметров уже работающего порта можно использовать функцию GetCommState (получить статус порта). Эта функция возвращает handle порта, его структуру (PortStruct) и претензий не вызывает. Для передачи данных по последовательному порту можно использовать две функции API: TransmitCommaChar (послать символ через СОМ-порт), которая выводит в порт один байт или WriteFile (запись файла), которая может выводить как массив (буфер) байт, так и всего один байт. Эти функции не вызывают претензий и работают правильно. Чтение информации из порта возможно только функцией ReadFile. Эта функция может прочитать из порта либо один байт, либо массив (буфер) байт только в том случае, если этот байт (или массив байт) поступил в СОМ-порт. В противном случае функция возвращает ошибку и может, кроме того, «повесить» компьютер на неопределенно долгое время, если байт в порт не поступал. При чтении, например, одного байта, размер входного буфера уменьшается на 1. Размер входного буфера указывается в функции SetupComm (установка СОМ-порта), но если, например, в порт поступает байт больше, чем указанный размер буфера (например 10), то байты не теряются, а накапливаются в буфере, и размер буфера растет. Этот факт является грубой ошибкой, которая может привести к непредсказуемым результатам в работе операционной системы. Функция PurgeComm (очистка СОМ-порта) предусмотрена для очистки входного буфера от поступивших байт, если, например, они не нужны. Очистить входной буфер можно, применив ту же функцию ReadFile. Если указать количество байт для чтения равным 1, то для очистки буфера размером в 10 байт необходимо 10 раз вызвать функцию ReadFile, если же равным 10, то эту функцию необходимо вызвать 1 раз. Может возникнуть вопрос: а зачем вообще очищать входной буфер? Если в порт приходит 10 байт, то и читать нужно 10 байт. Здесь несколько причин. Во-первых, одним из аргументов функции CreateFileA, открывающей СОМ-порт, является 5-й по счету параметр (всего их 7), который именуется CreationDisposition (создание диспозиции) и который должен принимать значение Open_Existing (т. е. открыть существующий). Это означает, что открывается СОМ-порт со своим, уже построенным, контрольным блоком (DCB), у которого все параметры приняты по умолчанию и, в частности, состояния линий квитирования DTR и RTS, как правило, установлены в высокий уровень. После открытия порта (т. е. после того как отработала функция CreateFileA), но перед тем как будут выполнены функции BuildCommDCBA и SetCommState, в которых, например, линии DTR и RTS должны быть в состоянии сброса (т.е. низкого уровня), эти линии установятся в высокий уровень и будут в нем находиться до тех пор, пока функции BuildCommDCBA и SetCommState не отработают. После того как функции BuildComntDCBA и SetCommState отработают, состояния линий DTR и RTS установятся уже в низкий уровень. Таким образом, уровни напряжений на линиях DTR и RTS кратковременно перейдут из низкого состояния в высокий и обратно. Если используется гальванически развязанный интерфейс с питанием от линий RTS и DTR, то на линии RXD появятся несколько импульсов. Поскольку это — линия данных, то, как следствие, в буфер СОМ-порта будут записаны несколько байт (1-3), которые являются мусором и которые необходимо сбросить (т. е. очистить от них приемный буфер). Сколько этих байт будет записано и сколько раз надо применить функцию RеаdFile, чтобы очистить буфер с точностью сказать невозможно. Если, предположим, записалось 3 байта а функцию ReadFile применили 2 раза, то в буфере будет находится один неверный байт. Если же записано 2 байта а функцию ReadFile применили 3 раза, то компьютер «повиснет» и будет ждать прихода третьего байта. Более просто и надежно работу с COM-интерфейсом можно реализовать, используя предопределенный набор классов платформы .NETFramework. Для взаимодействия с COM-портом служит объект SerialPort. При этом последовательность действий при программировании интерфейса будет следующая: Создать объект класса SerialPort: SerialPort comport = new SerialPort(); Задать обработчик для события получения данных: comport.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); Задать параметры порта: comport.BaudRate = comChatConnection.ConnectionProperties.BaudRate; comport.DataBits = 8; comport.StopBits = StopBits.One; comport.Parity = Parity.None; comport.DtrEnable = true; comport.PortName = string.Format("COM{0}", comChatConnection.ConnectionProperties.PortNumber); comport.WriteBufferSize = 12; comport.RtsEnable = true; comport.ReadTimeout = comChatConnection.ConnectionProperties.ReadTimeout; comport.WriteTimeout = comChatConnection.ConnectionProperties.WriteTimeout; Открыть порт: comport.Open(); Работать с портом: отправлять и получать данные. Чтобы послать данные через COM-порт необходимо выполнить следующую последовательность операций: byte[] data = new byte[length]; comport.Write(data, 0, data.Length); Чтобы получить данные из COM-порта необходимо выполнить следующую последовательность операций: byte[] data = new byte[comport.BytesToRead]; comport.Read(data, 0, data.Length); В средах BorlandC++ Builder и Delphi для работы с последовательным интерфейсом могут использоваться библиотека SerialGate.dll, которая содержит описание класса SerialGate, применяя методы которого можно выполнять наиболее востребованные действия по работе с портом (прием/передача данных, управление линиями взаимодействия, определение доступных портов в системе и т.д.), и бесплатный компонент TBComPort. Указанные dll и компонент находятся в подкаталоге COM каталога с методическими указаниями к данной лабораторной работе. Последовательность действий при использовании библиотеки SerialGate.dll при программировании последовательного интерфейса следующая: Создать объект класса SerialGate: SerialGate sg = new SerialGate(); Открыть порт: Используется функция bool Open (int port, int baud).Она открывает доступ к COM-порту с номером port на скорости baud байт/c. Если указанный порт существует и не занят другим приложением в данный момент, функция вернет true, иначе false. Если, к примеру, параметр port был указан как 3, то функция попытается открыть доступ к COM порту с именем COM3. Функция корректно работает и с виртуальными портами. boolb = sg.Open(1, 19200); if(b == true) { //успешно } else { //неуспешно } Чтобы послать данные через COM-порт необходимо вызвать следующую функцию: int Send (char *buff, int szBuff). Функция записывает в ранее открытый порт szBuff байт данных из буфера buff. Возвращает число успешно записанных байт данных в порт. intSendCounter = sg.Send(buff, sizeof(buff)); if(SendCounter != sizeof(buff)) { //не все данные были записаны в порт} Чтобы получить данные из COM-порта необходимо вызвать следующую функцию: int Recv (char *buff, int szBuff);Читает из ранее открытого порта szBuff байт данных и помещает их в буфер buff. Возвращает число реально прочитанных байт данных. intRcvCounter = sg.Recv(buff, sizeof(buff)); if(RcvCounter != sizeof(buff)) { //прочли меньше чем есть } Чтобы получить данные о COM-портах, распознанных операционной системой необходимо вызвать следующую функцию: void GetPortsInfo(PortInfo* pi);Функция заполняет переданную ей структуру PortInfo информацией об установленных в системе COM портах. Чтобы очистить входной и выходной буферы данных COM-порта следует использовать следующую функцию: void Clean ( ); sg.Clean(); Чтобы закрыть ранее установленное соединение с COM-портом нужно использовать следующую функцию: void Close ( ); sg.Close(); 3. Оборудование Персональный компьютер с установленной операционной системой MSWindows 2000 SP4 или XPSP2 и средами разработки ПО BorlandC++ Builder или MSVisualStudio 2005. Эмулятор виртуальных COM-портов VirtualSerialPortDriver (находится в подкаталоге COM каталога с методическими указаниями к данной лабораторной работе). 4. Задание на работу 1. Изучить теоретические положения по данной лабораторной работе. 2. Установить в системе эмулятор виртуальных COM-портов (VirtualSerialPortDriver). 3. Создать два виртуальных COM-порта и эмулировать связь между ними. 4. Разработать программное обеспечение для передачи данных через последовательный интерфейс. Вариантами заданий могут быть следующие: – реализовать чат, функционирующий через COM-порт; – организовать пересылку файлов с одного компьютера на другой через COM-порт; – прочитать и изменить параметры заданного COM-порта. 5. Оформление отчета Отчет должен содержать: название и цель работы; вариант задания; результаты работы созданного программного обеспечения; листинг программы, реализующей задание. 6. Контрольные вопросы 6.1. Объясните назначение и характерные особенности последовательного интерфейса. 6.2. Дайте описание сигналов последовательного интерфейса DB-9. 6.3. В чем состоит разница между синхронной и асинхронной передачей данных через COM-порт? 6.4. Каким образом реализуется контроль правильности передачи данных через последовательный интерфейс? 6.5. Перечислите основные регистры COM-порта и дайте их описание. 6.6. Каким образом осуществляется программирование COM-порта на низком уровне? 6.7. Какие возможности предоставляет WindowsAPI для работы с последовательным портом? 6.8. Приведите достоинства и недостатки функций WindowsAPI, реализующих обмен данными через COM-порт. 6.9. Какие возможности предоставляет .NETFramework по работе с последовательным интерфейсом? 6.10. Дайте описание функций библиотеки SerialGate.dll. Библиографический список 1. Фролов А., Фролов Г Аппаратное обеспечение IBMPC. т. 2, кн. 1, М: Диалог МИФИ, 1992. -208 с. 2. Фролов А., Фролов Г Аппаратное обеспечение IBMPC. т. 2, кн. 2, М: Диалог МИФИ, 1992. -220 с. 3. MSDN 2005. Электронная документация Microsoft для разработчиков программного обеспечения. – 200000 с. 4. Кузьминов А.Ю. Интерфейс RS-232: связь между компьютером и микроконтроллером. – М.: Радио и связь, 2004. – 168 с. 5. Рихтер Дж. Программирование на платформе Microsoft .NET Framework. – М.: Русская редакция, 2003. – 511 с. |