|
| ||||||||||||
| ||||||||||||
|
2004 г.
Работа с последовательными портамиЮрий Горский, Издательский Дом "КОМИЗДАТ" В настоящее время существует множество устройств, которые обмениваются с компьютером информацией через последовательный порт (COM1, COM2) по протоколу RS-232. Причем такие устройства разрабатывают до сих пор и, я уверен, будут разрабатывать и в дальнейшем. Ведь несмотря на недостатки такой связи: медленная скорость обмена информацией, ограничение на длину соединительных линий — существует и немало достоинств: программная поддержка протокола RS-232 и ему подобных многими периферийными устройствами, специализированными микросхемами, низкая стоимость, минимальное количество соединительных проводов, простота. Но, как это ни странно, информации по работе с последовательными портами в программах под Win32 очень мало. Материал этой статьи основан на статье Олега Титова “Работа с коммуникационными портами (COM и LPT) в программах для Win32”. Автором очень подробно описаны функции для работы с коммуникационными портами, основное внимание уделено синхронному обмену информацией. Мы же рассмотрим вариант обмена между компьютером и периферийным устройством в асинхронном режиме (как правило, используемом наиболее часто) — причем для простейшего соединения по трем проводам, без использования управляющих сигналов. Таким же образом можно организовать связь между двумя компьютерами (хотя бы для проверки работы своей программы). Начнем с главного: с последовательными портами в Win32 работают как с файлами. Причем используют только функции API Win32. Начинается работа с открытия порта как файла, причем для асинхронного режима ввода-вывода возможен только один вариант: HANDLE handle = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); Других вариантов быть не может, поэтому не будем рассматривать параметры этой функции подробно, единственное, что можно сделать — это заменить “COM1” на “COM2”. Больше последовательных портов на компьютере, как правило, нет. При успешном открытии порта функция возвращает дескриптор handle, с которым и будем работать в дальнейшем. При неудачном открытии порта функция вернет значение INVALID_HANDLE_VALUE. Настройка портаПолучив доступ к порту, необходимо его настроить — задать скорость обмена, формат данных, параметры четности и т. д. Основные параметры последовательного порта задают структуры DCB и COMMTIMEOUTS. Структура DCB содержит основные параметры порта и, кроме этого, довольно много специфических полей. typedef struct _DCB {
Мы рассмотрим назначение только некоторых основных полей этой структуры, используемых для нашего случая ввода-вывода, так как многие поля можно заполнить значениями “по умолчанию”, пользуясь функцией GetCommState: BOOL GetCommState(
Таким образом, нет необходимости вникать во все тонкости структуры. После этого некоторые поля DCB все же придется заполнить вручную, а именно: BaudRate — скорость передачи данных. Возможно указание следующих констант: CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400, CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000.Можно просто указать соответствующее число, например 9600, но предпочтительнее все-таки пользоваться символическими константами. ByteSize — определяет число информационных бит в передаваемых и принимаемых байтах. Может принимать значение 4, 5, 6, 7, 8. Parity — определяет выбор схемы контроля четности. Данное поле должно содержать одно из следующих значений:
StopBits — задает количество стоповых бит. Поле может принимать следующие значения:
После того как все поля структуры DCB заполнены, необходимо произвести конфигурирование порта, вызвав функцию SetCommState: BOOL SetCommState(
В случае успешного завершения функция вернет отличное от нуля значение, а в случае ошибки — нуль. Второй обязательной структурой для настройки порта является структура COMMTIMEOUTS. Она определяет параметры временных задержек при приеме-передаче. Вот описание этой структуры: typedef struct _COMMTIMEOUTS {
Поля структуры COMMTIMEOUTS имеют следующие значения:
Немного поподробнее о тайм-аутах. Пусть мы считываем из порта 50 символов со скоростью 9 600 бит/с. Если при этом используется 8 бит на символ, дополнение до четности и один стоповый бит, то на один символ в физической линии приходится 11 бит (включая стартовый бит). Значит, 50 символов на скорости 9 600 бит/с будут приниматься 50x11/9600=0,0572916 с или примерно 57,3 миллисекунды, при условии нулевого интервала между приемом последовательных символов. Если же интервал между символами составляет примерно половину времени передачи одного символа, т. е. 0,5 миллисекунд, то время приема будет 50x11/9600+49x0,0005=0,0817916 с или примерно 82 миллисекунды. Если в процессе чтения прошло более 82 миллисекунд, то мы вправе предположить, что произошла ошибка в работе внешнего устройства и можем прекратить считывание, тем самым избежав зависания программы. Это и есть общий тайм-аут операции чтения. Аналогично существует и общий тайм-аут операции записи. Формула для вычисления общего тайм-аута операции, например, чтения, выглядит так: NumOfChar x ReadTotalTimeoutMultiplier + ReadTotalTimeoutConstant где NumOfChar — число символов, запрошенных для операции чтения. В нашем случае тайм-ауты записи можно не использовать и установить их равными нулю. После заполнения структуры COMMTIMEOUTS, необходимо вызвать функцию установки тайм-аутов: BOOL SetCommTimeouts(
Поскольку операции передачи-приема ведутся на малой скорости, используется буферизация данных. Для задания размера буфера приема и передачи необходимо воспользоваться функцией: BOOL SetupComm(
Допустим, вы обмениваетесь с внешним устройством пакетами информации размером 1024 байта, тогда разумным размером буферов будет значение 1200. Функция SetupComm интересна тем, что она может просто принять ваши размеры к сведению, внеся свои коррективы, либо вообще отвергнуть предложенные вами размеры буферов — в таком случае эта функция завершится ошибкой. Приведу пример открытия и конфигурирования последовательного порта COM1. Для краткости — без определения ошибок. В данном примере порт открывается для работы со скоростью 9 600 бит/c, используется 1 стоповый бит, бит четности не используется: #include
После открытия порта первым делом необходимо сбросить его, так как в буферах приема и передачи может находиться “мусор”. Поэтому в конце примера мы применили ранее не известную нам функцию PurgeComm: BOOL PurgeComm(
Эта функция может выполнять две задачи: очищать очереди приема-передачи в драйвере или же завершать все операции ввода-вывода. Какие именно действия выполнять, задается другим параметром:
Настало время для рассмотрения непосредственно операций чтения-записи для порта. Как и для работы с файлами, используются функции ReadFile и WriteFile. Вот их прототипы: BOOL ReadFile(
Рассмотрим назначение параметров этих функций:
В случае нормального завершения функции возвращают значение, отличное от нуля, в случае ошибки — нуль. Приведу пример операции чтения и записи: #include В этом примере мы использовали две неизвестные нам ранее структуры COMSTAT и OVERLAPPED, а также функцию ClearCommError. Для нашего случая связи “по трем проводам” структуру OVERLAPPED можно не рассматривать (просто использовать, как в примере). Прототип функции ClearCommError имеет вид: BOOL ClearCommError(
Эта функция сбрасывает признак ошибки порта (если таковая имела место) и возвращает информацию о состоянии порта в структуре COMSTAT: typedef struct _COMSTAT
Нам могут пригодиться два поля этой структуры:
Остальные поля данной структуры содержат информацию об ошибках. И наконец, после завершения работы с портом его следует закрыть. Закрытие объекта в Win32 выполняет функция CloseHandle: BOOL CloseHandle(
На нашем сайте вы можете найти полный текст класса для работы с последовательным портом в асинхронном режиме “по трем проводам”, а также пример программы с использованием этого класса. Все это написано под Builder С++, но, поскольку используются только функции API Win32, текст программы легко изменить под любой компилятор С++. Возможно также, что класс написан не совсем “по правилам” — прошу извинить, автор не является “правильным” программистом и пишет так, как ему удобно J . Если у вас возникли вопросы по поводу использования функций, рассмотренных выше, вы всегда сможете обратиться к справочной информации по Win32. А если возникнет необходимость более полно использовать последовательные порты (например, использовать различные управляющие сигналы) прочтите статью Олега Титова “Работа с коммуникационными портами (COM и LPT) в программах для Win32”. Желаю Вам успехов! |
|
CITForum © 1997–2025