Автор работы: Пользователь скрыл имя, 01 Ноября 2010 в 14:15, Не определен
В настоящее время существует множество устройств, которые обмениваются с компьютером информацией через последовательный порт (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 {
DWORD DCBlength; // sizeof(DCB)
DWORD BaudRate; // current baud rate
DWORD fBinary:1; // binary mode, no EOF check
DWORD fParity:1; // enable parity checking
DWORD fOutxCtsFlow:1; // CTS output flow control
DWORD fOutxDsrFlow:1; // DSR output flow control
DWORD fDtrControl:2; // DTR flow control type
DWORD fDsrSensitivity:1; // DSR sensitivity
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
DWORD fOutX:1; // XON/XOFF out flow control
DWORD fInX:1; // XON/XOFF in flow control
DWORD fErrorChar:1; // enable error replacement
DWORD fNull:1; // enable null stripping
DWORD fRtsControl:2; // RTS flow control
DWORD fAbortOnError:1; // abort reads/writes on error
DWORD fDummy2:17; // reserved
WORD wReserved; // not currently used
WORD XonLim; // transmit XON threshold
WORD XoffLim; // transmit XOFF threshold
BYTE ByteSize; // number of bits/byte, 4-8
BYTE Parity; // 0-4=no,odd,even,mark,space
BYTE StopBits; // 0,1,2 = 1, 1.5, 2
char XonChar; // Tx and Rx XON character
char XoffChar; // Tx and Rx XOFF character
char ErrorChar; // error replacement character
char EofChar; // end of input character
char EvtChar; // received event character
WORD wReserved1; // reserved; do not use
} DCB;
Мы рассмотрим
назначение только некоторых основных
полей этой структуры, используемых
для нашего случая ввода-вывода, так
как многие поля можно заполнить значениями
“по умолчанию”, пользуясь функцией GetCommState:
BOOL GetCommState(
HANDLE hFile,
LPDCB lpDCB
);
Таким образом,
нет необходимости вникать во
все тонкости структуры. После этого
некоторые поля 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 — определяет
число информационных бит в
передаваемых и принимаемых
Parity — определяет выбор схемы контроля четности. Данное поле должно содержать одно из следующих значений:
EVENPARITY — дополнение до четности;
MARKPARITY — бит четности всегда равен 1;
NOPARITY — бит четности отсутствует;
ODDPARITY — дополнение до нечетности;
SPACEPARITY — Бит
четности всегда 0.
StopBits — задает
количество стоповых бит. Поле
может принимать следующие
ONESTOPBIT — один стоповый бит;
ONE5STOPBIT — полтора стоповых бита (практически не используется);
TWOSTOPBIT — два
стоповых бита.
После того как
все поля структуры DCB заполнены, необходимо
произвести конфигурирование порта, вызвав
функцию SetCommState:
BOOL SetCommState(
HANDLE hFile,
LPDCB lpDCB
);
В случае успешного
завершения функция вернет отличное от
нуля значение, а в случае ошибки — нуль.
Второй обязательной
структурой для настройки порта
является структура COMMTIMEOUTS. Она определяет
параметры временных задержек при
приеме-передаче. Вот описание этой
структуры:
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
Поля структуры COMMTIMEOUTS имеют следующие значения:
ReadIntervalTimeout —
максимальное временной
ReadTotalTimeoutMultiplier
— задает множитель (в
ReadTotalTimeoutConstant — задает константу (в миллисекундах), используемую для вычисления общего тайм-аута операции чтения. Для каждой операции чтения данное значение плюсуется к результату умножения ReadTotalTimeoutMultiplier на количество запрошенных для чтения символов. Нулевое значение полей ReadTotalTimeoutMultiplier и ReadTotalTimeoutConstant означает, что общий тайм-аут для операции чтения не используется.
WriteTotalTimeoutMultiplier
— задает множитель (в
WriteTotalTimeoutConstant —
задает константу (в
Немного поподробнее
о тайм-аутах. Пусть мы считываем
из порта 50 символов со скоростью 9 600 бит/с.
Если при этом используется 8 бит
на символ, дополнение до четности и
один стоповый бит, то на один символ в
физической линии приходится 11 бит
(включая стартовый бит). Значит, 50 символов
на скорости 9 600 бит/с будут приниматься
50x11/9600=0,0572916 с
или примерно 57,3
миллисекунды, при условии нулевого
интервала между приемом
50x11/9600+49x0,0005=0,0817916
с
или примерно 82
миллисекунды. Если в процессе чтения
прошло более 82 миллисекунд, то мы вправе
предположить, что произошла ошибка в
работе внешнего устройства и можем прекратить
считывание, тем самым избежав зависания
программы. Это и есть общий тайм-аут операции
чтения. Аналогично существует и общий
тайм-аут операции записи.
Формула для
вычисления общего тайм-аута операции,
например, чтения, выглядит так:
NumOfChar x ReadTotalTimeoutMultiplier
+ ReadTotalTimeoutConstant
где NumOfChar — число
символов, запрошенных для операции
чтения.
В нашем случае
тайм-ауты записи можно не использовать
и установить их равными нулю.
После заполнения
структуры COMMTIMEOUTS, необходимо вызвать
функцию установки тайм-аутов:
BOOL SetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
Поскольку операции
передачи-приема ведутся на малой скорости,
используется буферизация данных. Для
задания размера буфера приема и передачи
необходимо воспользоваться функцией:
BOOL SetupComm(
HANDLE hFile,
DWORD dwInQueue,
DWORD dwOutQueue
);
Допустим, вы обмениваетесь
с внешним устройством пакетами
информации размером 1024 байта, тогда разумным
размером буферов будет значение 1200. Функция
SetupComm интересна тем, что она может просто
принять ваши размеры к сведению, внеся
свои коррективы, либо вообще отвергнуть
предложенные вами размеры буферов —
в таком случае эта функция завершится
ошибкой.
Приведу пример
открытия и конфигурирования последовательного
порта COM1. Для краткости — без
определения ошибок. В данном примере
порт открывается для работы со скоростью
9 600 бит/c, используется 1 стоповый бит,
бит четности не используется:
#include
. . . . . . . . . .
HANDLE handle;
COMMTIMEOUTS CommTimeOuts;
DCB dcb;
handle = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
SetupComm(handle, SizeBuffer, SizeBuffer);
GetCommState(handle, &dcb);
dcb.BaudRate = CBR_9600;
dcb.fBinary = TRUE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fDsrSensitivity = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = FALSE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = 1;
SetCommState(handle, &dcb);
CommTimeOuts.
CommTimeOuts.
// значений этих тайм – аутов вполне хватает для уверенного приема
// даже на скорости 110 бод
CommTimeOuts.
// используется в данном случае как время ожидания посылки
CommTimeOuts.
CommTimeOuts.
SetCommTimeouts(handle, &CommTimeOuts);
PurgeComm(handle, PURGE_RXCLEAR);
PurgeComm(handle, PURGE_TXCLEAR);
После открытия
порта первым делом необходимо сбросить
его, так как в буферах приема
и передачи может находиться “мусор”.
Поэтому в конце примера мы применили
ранее не известную нам функцию PurgeComm:
Информация о работе Программирование последовательных портов