ПРОГРАММИРОВАНИЕ ИНТЕРФЕЙСА RS-232 ИНТЕРФЕЙС RS-232 Информация по линиям интерфейса RS-232 передается асинхронно последовательным кодом. Это означает, что передатчик посылает байт данных бит за битом. Для такой последовательной передачи требуется только две линии (два провода). Стандарт RS-232 использует несимметричные передатчики и приемники – сигнал передается относительного общего провода. Интерфейс не обеспечивает гальванической развязки устройств, т.е. при подключении устройств одно или оба порта должны быть обесточены, в противном случае возможно повреждение портов вследствие разницы потенциалов. Логической единице соответствует напряжение на входе приемника в диапазоне –12...-3В. Логическому нулю соответствует диапазон +3...+12В. Диапазон –3...+3В – зона нечувствительности, обусловливающая гистерезис приемника: состояние линии считается измененным только после пересечения порога (см. рис. 1). Рис. 1. Уровни сигналов интерфейса RS-232 В стандарте при передаче слов (посылок) информации реализуется так называемый старт-стопный метод, т.е. каждое передаваемое слово начинается специальным старт-битом, позволяющим приемнику определить начало передачи слова. Затем передается информационный байт младшими битами вперед бит за битом, т.е. первым передается младший значащий бит (МЗБ), а последним старший (СЗБ). После битов данных может следовать бит контроля (бит проверки четности/нечетности). Иногда бит проверки может отсутствовать. Бит контроля предназначен для повышения уровня достоверности при приеме данных и показывает четно или не четно в байте информации кол-во нулей и единиц. Завершение передачи слова отмечается специальными стоп-битами (см. рис. 2). Старт бит МЗБ Биты данных Бит контроля Стоп биты CЗБ Рис.2. Формат слова в RS-232 Таким образом, формат слова определяет следующие особенности переноса информации через интерфейс: 1. число битов, используемых для кодирования самого переносимого символа; 2. наличие или отсутствие контроля по четности; 3. способ формирования контрольного бита; 4. число стоп-битов. Как рассматривалось ранее, старт бит является для приемной стороны сигналом начала слова. По этому сигналу на приемной стороне запускается в работу специальный аппаратный узел — сдвиговый регистр, который «собирает» в параллельный код принятое бит за битом слово информации. Биты передаются с известной приемнику и передатчику частотой, измеряемой в бодах в секунду. Боды - это количество передаваемых изменений сигнала (бит) в секунду. При этом учитываются и старт/стопные биты, а также бит четности. Иногда используется другой термин - биты в секунду (bps). Здесь имеется в виду эффективная скорость передачи данных, без учета служебных битов. Передатчик и приемник используют разные источники синхронизации, которые работают с близкой, но все-таки различающейся частотой. Сильное расхождение частот приемника и передатчика вызывает возникновение специфической для асинхронной связи ошибки, называемой ошибкой кадрирования. Так же ошибка кадрирования может возникать тогда, когда формат слова приемника и передатчика не согласованы. Рассчитаем на примере допустимый сдвиг интервала одного бита в посылке: пусть скорость приемника и передатчика 9600 бод/с, формат слова 1 старт бит, 1 стоп бит, 8 бит информации, контрольного бита нет, тогда время 1 бода t = 1 / 9600 ~ 0,1042 мс. Учитывая, что съем данных происходит в середине бода, получаем что максимальный сдвиг за 10 бод не должен превышать времени половины бода, т.е. 0,0521 мс, таким образом, сдвиг времени на бод должен быть не более 0,0521 / 10 = 0,00521 мс. На практике время сдвига должно быть значительно меньше, вследствие влияния емкостной и индуктивных составляющих линии передачи. ПОРТЫ АСИНХРОННОГО АДАПТЕРА ПК Современный персональный компьютер зачастую не оснащается последовательными портами, тем не менее, для оснащения современных компьютеров последовательными портами используют: PCI платы расширения, содержащие от 1 до 8 портов последовательной передачи данных. Зачастую такие платы так же оснащаются параллельным интерфейсом LPT. USB-to-COM переходники, имеющие с одной стороны полноценный последовательны порт стандарта RS-232, с другой стороны USB вилку для подключения к персональному компьютеру. В основе последовательного порта передачи данных лежит микросхема Intel 16550. Это универсальный асинхронный приемо-передатчик (UART Universal Asynchronous Receiver Transmitter). Микросхема содержит несколько интерфейсов RS-232 и соответствующее количество внутренних регистров, предназначенных для управления работой портами ввода/вывода. Микросхема 16550 содержит регистры передатчика и приемника данных. При передаче байта он записывается в буферный регистр передатчика, откуда затем переписывается в сдвиговый регистр передатчика. Байт «выдвигается» из сдвигового регистра по битам. Аналогично имеются сдвиговый и буферный регистры приемника. Программный доступ можно осуществлять только к буферным регистрам, копирование информации в сдвиговые регистры, и процесс сдвига выполняется микросхемой 16550 автоматически. Внешние устройства подключаются к порту ввода/вывода через разъем DB25P (имеющий 25 выводов, рис. 3) или DB9P (имеющий 9 выводов, рис. 4). В табл. 2 приведена разводка разъема последовательной передачи данных DB25P и DB9P. Рис.3. Разъем DB25P Рис.4. Разъем DB9P Обозначен ие цепи RS-232 Номер контакта DB9P DP25P - 1 5 7 3 2 2 3 7 4 8 5 6 6 4 20 1 8 9 22 PG SG TD RD RTS CTS DSR DTR DCD RI Таблица №2. Описание сигналов интерфейса RS-232 Направлен ие Назначение сигнала передачи Вход/выхо д Protected Ground. Защитное заземление, соединяется с корпусом устройства и экраном кабеля Signal Ground. Сигнальная земля - уровень напряжения относительно которого действуют уровни сигналов. Transmit Data. Последовательные данные – выход выход передатчика Receive Data. Последовательные данные - вход вход приемника. Request To Send. Выход запроса передачи данных. Состояние «включено» уведомляет о выход наличии у терминала данных для передачи. Clear To Send – разрешение терминалу вход передавать данные. Состояние «выключено» запрещает передачу. Data Set Read. Готовность устройства на другой вход стороне канала к обмену данными Data Terminal Ready. Готовность к обмену выход данными. Data Carrier Detected. Вход сигнала обнаружения несувход щей удаленного модема Ring Indicator. Вход индикатора вызова (звонка). вход В коммутируемом канале этим сигналом модем сигнализирует о принятии вызова. Рассмотрим пример обмена сигналами между сторонами интерфейса RS232 модемом и ПК: 1. Компьютер после включения питания выставляет сигнал готовности DTR, который постоянно удерживается активным. Если модем включен и исправен, он отвечает компьютеру сигналом DSR. Этот сигнал служит подтверждением того, что DTR принят, и информирует компьютер о готовности модема к приему информации; 2. Если компьютер получил сигнал DSR и хочет передать данные он выставляет сигнал RTS; 3. Если модем готов принять данные, он отвечает сигналом CTS. C этого момента адаптер компьютера может бит за битом передавать информацию по линии TD; 4. Получив порцию данных, модем может сбросить свой сигнал CTS, информируя компьютер о необходимости «притормозить» передачу следующего байта, например из-за переполнения внутреннего буфера; 5. Программа компьютера, обнаружив сброс CTS, прекращает передачу данных, ожидая повторного появления CTS. Когда модему необходимо передать данные в компьютер он выставляет сигнал DCD. Программа компьютера, принимающая данные, обнаружив этот сигнал, читает приемный регистр, в который сдвиговый регистр «собрал» биты, принятые по линии приема данных RD. Когда для связи используются только линии TD, RTS и DTR (от компьютера к модему), то компьютер не может попросить модем «повременить» с передачей следующего байта. Как следствие, существует опасность переопределения помещенного ранее в приемном регистре байта данных вновь «собранным» байтом. Поэтому при приеме информации компьютер должен очень быстро освобождать приемный регистр адаптера. В полном наборе сигналов RS-232 есть линии, которые могут аппаратно «приостановить» модем. ПРОГРАММИРОВАНИЕ ПОСЛЕДОВАТЕЛЬНЫХ ПОРТОВ В ОС СЕМЕЙСТВА WINDOWS NT (WIN32 API) Работа с последовательными СОМ-портами в ОС семейства Windows NT значительно отличается от работы с портами в DOS. Основными отличиями являются: запрещен прямой доступ к регистрам контроллера портов (возможен только в режиме ядра); запрещена работа с одним и тем же портом более чем двум программам (неродственным процессам); Но, несмотря на перечисленные запреты, инициализация и работа с портом значительно проще. Для того чтобы инициализировать порт, достаточно вызвать функцию CreateFile с соответствующими аргументами. После этого можно записывать и считывать данные посредством стандартных функций WriteFile и ReadFile. Кроме того, для настройки портов и последующего управления ими предназначен целый ряд дополнительных функций. Рассмотрим основные моменты программирования портов подробнее. Перед началом работы необходимо открыть порт и настроить его на требуемый режим работы (для СОМ-порта). В листинге №1 показан пример инициализации COM1 порта. Листинг №1. Инициализация COM1 порта #include <windows.h> // объявляем структуру для конфигурации последовательного порта DCB dcb; ZeroMemory ( &dcb, sizeof ( DCB)); // дескриптор порта HANDLE hCom 1 = NULL; // пишем функцию инициализации порта СOМ1 bool Init_COMl () { // открываем порт СOM1 (для COM2 "COM2", для LPT1 "LPT1") hCom_l = CreateFile ( "COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if ( hCom_l == INVALID_HANDLE_VALUE) // если порт не удалось открыть return false; // выходим из функции // если порт успешно открыт, получаем его состояние if ( !GetCommState ( hCom_l, &dcb)) { // если не удалось получить статус порта, выходим из функции CloseHandle ( hCom_l); return false; // выходим из функции } // настраиваем параметры порта dcb.BaudRate = CBR_19200; // скорость передачи 19 200 бод dcb.ByteSize =8; // размер байта данных dcb.StopBits = ONESTOPBIT; // один стоповый бит dcb.Parity = NOPATITY; // контроля четности нет // сохраняем новые параметры конфигурации для порта if ( !SetCommState ( hCom_l, &dcb)) { // если не удалось настроить порт, выходим из функции CloseHandle ( hCom_l); return false; // выходим из функции } return true; } Теперь, когда COM1 порт открыт и сконфигурирован для работы, нужно настроить обработку сигналов DSR и CTS. Пример кода для активизации уведомлений для последовательного порта показан в листинге №2. Листинг №2. Настройка уведомляющих событий сигналов DSR и CTS // объявляем структуру для асинхронного ввода-вывода данных //и помещаем ее в глобальную область видимости OVERLAPPED over; ZeroMemory (&over, sizeof(OVERLAPPED)); // пишем функцию настройки событий bool SetEventCOM () // настраиваем мониторинг за определенными событиями порта if ( !SetCommMask ( hCom_l, EV_DSR | EV_CTS)) { // если не удалось настроить порт, выходим из функции CloseHandle( hCom_l); return false; // выходим из функции } // создаем объект события over.hEvent = CreateEvent ( NULL, FALSE, FALSE, NULL); if ( !over.hEvent) { // если не удалось создать событие, выходим из функции CloseHandle ( hCom_l); return false; // выходим из функции } return true; } На данный момент вся начальная подготовка для работы с портом закончена. Далее нужно написать функции чтения и записи данных и функцию управления последовательного порта. Примеры функций чтения и записи показаны в листинге №3. Листинг №3. Функции чтения и записи для работы с COM портов // функция чтения одного байта данных BYTE ReadByteCOM () { BYTE read = 0; DWORD dwByteRead = 0; do { // читаем байт из порта if ( !ReadFile ( hCom_l, &read, sizeof ( BYTE), &dwByteRead,NULL)) return 0; } while ( !dwByteRead); return read; // возвращаем прочитанный байт } // функция чтения массива данных DWORD ReadData_COM ( void* Data, unsigned int uNumBytes) { DWORD dwBytesRead - 0; if ( !ReadFile ( hCom_l, Data, uNumBytes, &dwBytesRead, NULL)) return dwBytesRead; // сколько удалось прочитать return dwBytesRead; // возвращаем полное число прочитанных байтов } // функция для записи одного байта bool WriteByteCOM ( BYTE bByte) { BYTE write = 0; DWORD dwByteWrite = 0; if ( !WriteFile ( hCom_l, &write, sizeof ( BYTE), SdwByteWrite,NULL)) return false; return true; } // функция для записи массива данных DWORD WriteDataCOM ( void* Data, unsigned int uNumBytes) { DWORD dwBytesWrite = 0; if ( !WriteFile ( hCom_l, Data, uNumBytes, SdwByteWrite, NULL)) return dwBytesWrite; // сколько удалось записать return dwBytesWrite; // возвращаем полное число записанных байтов } Пример общей функции работы с портом представлен в листинге №4. Листинг №4. Пример общей функции работы с COM портом // добавляем в глобальную область данных значение для сигнала DWORD dwSignal; void GeneralCOM () { // проверяем сигнал в линии if ( WaitCommEvent ( hCom_l, &dwSignal, &over)) { if ( dwSignal & EV_DSR) // данные готовы для чтения { // читаем байт из порта BYTE data = ReadByteCOM (); // сохраняем полученный байт куда-либо } if ( dwSignal & EV_CTS) { // можно писать данные в порт // передаем извне байт и пишем его в порт WriteByteCOM ( myByte); } } } // после завершения работы следует вызвать // функцию очистки ресурсов void CloseCOM () { if ( over.hEvent) { CloseHandle ( over.hEvent); // закрываем объект события over.hEvent = NULL; } if ( hCom_l) { CloseHandle ( hCom_l); // закрываем порт СOМ1 hCom = NULL; } } ЛИТЕРАТУРА 1. 2. 3. 4. 5. Агуров П. Последовательные интерфейсы ПК. Практика программирования. – СПб.: БХВ – Петербург, 2004. – 496 с. Ан П. Сопряжение ПК с внешними устройствами / Пей Ан; Пер. с англ. Мерещука П.В. – 2-е изд. стер. – М.: ДМК Пресс; СПб.: Питер, 2004. – 320 с.: ил. Гук М. Аппаратные интерфейсы ПК. Энциклопедия. – СПб.: Питер, 2002. – 528 с.: ил. Несвижский В.А. Программирование аппаратных средств в Windows. – СПб.: БХВ – Петербург, 2004. – 880 с.: ил. Смит Дж. Сопряжение компьютеров с внешними устройствами. Уроки реализации / Смит Дж.; Пер. с англ. – М.: Мир, 2000. – 266 с.