COM-порт в Windows (программирование)

Материал из Викиучебника — открытых книг для открытого мира
  Этот текст надо викифицировать. Пожалуйста, отформатируйте его согласно рекомендациям.

Написать программу, управляющую устройством через COM-порт, для MS-DOS не так сложно. С платформой Win32 дело обстоит сложнее. Но только на первый взгляд. Конечно напрямую работать с регистрами портов нельзя, Windows это не позволяет, зато можно не обращать внимания на тонкости различных реализаций (i8251, 16450, 16550A) и не возиться с обработкой прерываний.

Открытие порта[править]

С последовательными и параллельными портами в Win32 работают как с файлами. Для открытия порта используется функция CreateFile. Эта функция предоставляется Win32 API. Ее прототип выглядит так:

  HANDLE CreateFile(
     LPCTSTR               lpFileName,
     DWORD                 dwDesiredAccess,
     DWORD                 dwShareMode,
     LPSECURITY_ATTRIBUTES lpSecurityAttributes,
     DWORD                 dwCreationDistribution,
     DWORD                 dwFlagsAndAttributes,
     HANDLE                hTemplateFile
  );

lpFileName[править]

Указатель на строку с именем открываемого или создаваемого файла. Формат этой строки может быть очень «хитрым». В частности можно указывать сетевые имена для доступа к файлам на других компьютерах. Можно открывать логические разделы или физические диски и работать в обход файловой системы.

Последовательные порты имеют имена "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9". Для доступа к портам, чей номер больше 9, необходимо указывать имя порта как "\\.\COMx", где x - номер порта. Например, "\\.\COM72" (в нотации языка C/C++ строка будет выглядеть "\\\\.\\COM72"). Такой синтаксис подходит для любого номера порта. Точно так же они назывались в MS-DOS. Параллельные порты называются «LPT1», «LPT2» и так далее.

dwDesiredAccess[править]

Задает тип доступа к файлу. Возможно использование следующих значений:

  • 0 Опрос атрибутов устройства без получения доступа к нему.
  • GENERIC_READ Файл будет считываться.
  • GENERIC_WRITE Файл будет записываться.
  • GENERIC_READ|GENERIC_WRITE Файл будет и считываться и записываться.

dwShareMode[править]

Задает параметры совместного доступа к файлу. Коммуникационные порты нельзя делать разделяемыми, поэтому данный параметр должен быть равен 0.

lpSecurityAttributes[править]

Задает атрибуты защиты файла. Поддерживается только в Windows NT. Однако при работе с портами должен в любом случае равняться NULL.

dwCreationDistribution[править]

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

dwFlagsAndAttributes[править]

Задает атрибуты создаваемого файла. Также управляет различными режимами обработки. При работе с портом этот параметр должен быть или равным 0, или FILE_FLAG_OVERLAPPED. Нулевое значение используется при синхронной работе с портом, а FILE_FLAG_OVERLAPPED при асинхронной, или, другими словами, при фоновой обработке ввода/вывода. Подробнее про асинхронный ввод/вывод я расскажу позже.

hTemplateFile[править]

Задает описатель файла-шаблона. При работе с портами всегда должен быть равен NULL.

При успешном открытии файла, в данном случае порта, функция возвращает дескриптор (HANDLE) файла. При ошибке [[|INVALID HANDLE VALUE]]. Код ошибки можно получитить вызвав функцию [[|GetLastError]].

Закрытие порта[править]

Открытый порт должен быть закрыт перед завершением работы программы. В Win32 закрытие объекта по его дескриптору выполняет функция CloseHandle:

BOOL CloseHandle(
     HANDLE hObject
     );

При успешном завершении функция возвращает не нулевое значение, при ошибке нуль.

Пример открытия/закрытия на языке C[править]

   #include <windows.h>
   //. . .
   HANDLE Port;
   //. . .
   Port = CreateFile(L"\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
   if (Port == INVALID_HANDLE_VALUE) {
      MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
      ExitProcess(1);
   }
   //. . .
   CloseHandle(Port);
   //. . .

В данном примере открывается порт СОМ2 для чтения и записи, используется синхронный режим обмена. Проверяется успешность открытия порта, при ошибке выводится сообщение и программа завершается. Если порт открыт успешно, то он закрывается.

Структура DCB[править]

Основные параметры последовательного порта описываются структурой 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;

Эта структура содержит почти всю управляющую информацию, которая в реальности располагается в различных регистрах последовательного порта.

DCBlength[править]

Задает длину, в байтах, структуры 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. Эти константы соответствуют всем стандартным скоростям обмена. На самом деле, это поле содержит числовое значение скорости передачи, а константы просто являются символическими именами. Поэтому можно указывать, например, и CBR_9600, и просто 9600. Однако рекомендуется указывать символические константы, поскольку при компиляции программы проверяется корректность их имен.

fBinary[править]

Включает двоичный режим обмена. Win32 не поддерживает недвоичный режим, поэтому данное поле всегда должно быть равно 1, или логической константе TRUE (что предпочтительней). В Windows 3.1, если это поле было равно FALSE, включался текстовый режим обмена. В этом режиме поступивший на вход порта символ, заданный полем EofChar, свидетельствовал о конце принимаемых данных.

fParity[править]

Включает режим контроля четности. Если это поле равно TRUE, то выполняется проверка четности, при ошибке, в вызывающую программу, выдается соответствующий код завершения.

fOutxCtsFlow[править]

Включает режим слежения за сигналом [[|CTS]]. Если это поле равно [[|TRUE]] и сигнал [[|CTS]] сброшен, передача данных приостанавливается до установки сигнала CTS. Это позволяет подключеному к компьютеру прибору приостановить поток передаваемой в него информации, если он не успевает ее обрабатывать.

fOutxDsrFlow[править]

Включает режим слежения за сигналом [[|DSR]]. Если это поле равно TRUE и сигнал DSR сброшен, передача данных прекращается до установки сигнала DSR.

fDtrControl[править]

Задает режим управления обменом для сигнала [[|DTR]]. Поле может принимать следующие значения:

  • DTR_CONTROL_DISABLE         Сигнал DTR снимается при открытии порта. У открытого порта может быть изменён функцией EscapeCommFunction.
  • DTR_CONTROL_ENABLE          Сигнал DTR устанавливается при открытии порта. У открытого порта может быть изменён функцией EscapeCommFunction.
  • DTR_CONTROL_HANDSHAKE   Сигнал DTR автоматически устанавливается/снимается в ходе работы с портом. Не может быть изменён функцией EscapeCommFunction.

fDsrSensitivity[править]

Задает чувствительсть коммуникационного драйвера к состоянию линии [[|DSR]]. Если это поле равно TRUE, то все принимаемые данные игнорируются драйвером (коммуникационный драйвер расположен в операционной системе), за исключением тех, которые принимаются при установленом сигнале DSR.

fTXContinueOnXoff[править]

Задает, прекращается ли передача при переполнении приемного буфера и передаче драйвером символа XoffChar. Если это поле равно TRUE, то передача продолжается, несмотря на то, что приемный буфер содержит более XoffLim символов и близок к переполнению, а драйвер передал символ XoffChar для приостановления потока принимаемых данных. Если поле равно FALSE, то передача не будет продолжена до тех пор, пока в приемном буфере не останется меньше XonLim символов и драйвер не передаст символ XonChar для возобновления потока принимаемых данных. Таким образом это поле вводит некую зависимость между управлением входным и выходным потоками информации.

fOutX[править]

Задает использование XON/XOFF управления потоком при передаче. Если это поле равно TRUE, то передача останавливается при приеме символа XoffChar, и возобновляется при приеме символа XonChar.

fInX[править]

Задает использование XON/XOFF управления потоком при приеме. Если это поле равно TRUE, то драйвер передает символ XoffChar, когда в приемном буфере находится более XoffLim, и XonChar, когда в приемном буфере остается менее XonLim символов.

fErrorChar[править]

Указывает на необходимость замены символов с ошибкой четности на символ задаваемый полем ErrorChar. Если это поле равно TRUE, и поле fParity равно TRUE, то выполняется замена.

fNull[править]

Определяет действие выполняемое при приеме нулевого байта. Если это поле TRUE, то нулевые байты отбрасываются при передаче.

fRtsControl[править]

Задает режим управления потоком для сигнала RTS. Поле может принимать следующие значения:

  • RTS_CONTROL_DISABLE          Сигнал RTS снимается при открытии порта. У открытого порта может быть изменён функцией EscapeCommFunction.
  • RTS_CONTROL_ENABLE           Сигнал RTS устанавливается при открытии порта. У открытого порта может быть изменён функцией EscapeCommFunction.
  • RTS_CONTROL_HANDSHAKE   Сигнал RTS автоматически устанавливается/снимается в ходе работы с портом. Не может быть изменён функцией EscapeCommFunction. Сигнал RTS устанавливается, когда приемный буфер заполнен менее, чем на половину, и снимается, когда буфер заполняется более чем на три четверти.
  • RTS_CONTROL_TOGGLE          Задаёт, что сигнал RTS установлен, когда есть данные для передачи. Когда все символы из передающего буфера переданы, сигнал снимается.

fAbortOnError[править]

Задает игнорирование всех операций чтения/записи при возникновении ошибки. Если это поле равно TRUE, драйвер прекращает все операции чтения/записи для порта при возникновении ошибки. Продолжать работать с портом можно будет только после устранения причины ошибки и вызова функции ClearCommError.

fDummy2[править]

Зарезервировано и не используется.

wReserved[править]

Не используется, должно быть установлено в 0.

XonLim[править]

Задает минимальное число символов в приемном буфере перед посылкой символа XON.

XoffLim[править]

Определяет максимальное количество байт в приемном буфере перед посылкой символа XOFF. Максимально допустимое количество байт в буфере вычисляется вычитанием данного значения из размера приемного буфера в байтах.

ByteSize[править]

Определяет число информационных бит в передаваемых и принимаемых байтах. Число информационных бит может быть в диапазоне от 4 до 8.

Parity[править]

Определяет выбор схемы контроля четности. Данное поле должно содержать одно из следующих значений:

  • EVENPARITY     Дополнение до четности
  • MARKPARITY     Бит четности всегда 1
  • NOPARITY         Бит четности отсутствует
  • ODDPARITY       Дополнение до нечетности
  • SPACEPARITY   Бит четности всегда 0

StopBits[править]

Задает количество стоповых бит. Поле может принимать следующие значения:

  • ONESTOPBIT     Один стоповый бит
  • ONE5STOPBIT   Полтора стоповых бита
  • TWOSTOPBITS     Два стоповых бита

XonChar[править]

Задает символ XON используемый как для приема, так и для передачи. Обычно 0x11 (17).

XoffChar[править]

Задает символ XOFF используемый как для приема, так и для передачи. Обычно 0x13 (19).

ErrorChar[править]

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

EofChar[править]

Задает символ, использующийся для сигнализации о конце данных.

EvtChar[править]

Задает символ, использующийся для сигнализации о событии.

wReserved1[править]

Зарезервировано и не используется.

Замечания[править]

Если структура DCB содержит конфигурацию для последовательного порта, совместимого с 8250, то к значениям полей ByteSize и StopBits применяются следующие ограничения:

  • Количество информационных бит должно быть от 5 до 8.
  • Не допускается использование 5 информационных бит с 2 стоповыми битами, также как 6, 7 или 8 информационных бит с 1,5 стоповыми битами.

Заполнение структуры DCB[править]

Структура COMMTIMEOUTS[править]

winbase.h

typedef struct _COMMTIMEOUTS {
    DWORD ReadIntervalTimeout;          /* Maximum time between read chars. */
    DWORD ReadTotalTimeoutMultiplier;   /* Multiplier of characters.        */
    DWORD ReadTotalTimeoutConstant;     /* Constant in milliseconds.        */
    DWORD WriteTotalTimeoutMultiplier;  /* Multiplier of characters.        */
    DWORD WriteTotalTimeoutConstant;    /* Constant in milliseconds.        */
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

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

Чтобы операция ReadFile немедленно возвращала управление со всеми полученными данными (асинхронный режим) следует задавать следующие значения:

ReadIntervalTimeout=0xFFFFFFFF;
ReadTotalTimeoutConstant=0;
ReadTotalTimeoutMultiplier=0;

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

ReadTotalTimeoutConstant - Константа, используемая, чтобы вычислить полный (максимальный) период времени простоя для операций чтения, в миллисекундах. Для каждой операции чтения, это значение добавляется к произведению члена структуры ReadTotalTimeoutMultiplier и прочитанного числа байтов.

Значение нуля и для члена ReadTotalTimeoutMultiplier, и для члена ReadTotalTimeoutConstant указывает, что полное время простоя не используются для операций чтения.

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

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

Значение нуля и для члена WriteTotalTimeoutMultiplier, и для члена WriteTotalTimeoutConstant указывает, что полное время простоя не используются для операций записи.

Заполнение структуры COMMTIMEOUTS[править]

Вариант 1: (максимальная задержка при чтении и записи = TIMEOUT)

 	COMMTIMEOUTS CommTimeOuts;
 	CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
 	CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
 	CommTimeOuts.ReadTotalTimeoutConstant = TIMEOUT;
 	CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
 	CommTimeOuts.WriteTotalTimeoutConstant = TIMEOUT;

Вариант 2: Инициализация значениями (без задержки при чтении)

 	COMMTIMEOUTS CommTimeOuts={0xFFFFFFFF,0,0,0,1500};

Пример настройки порта[править]

Структура COMMPORT[править]

Стандартный диалог настройки порта[править]

Для настройки параметров COM - порта может быть вызвано штатное окно Windows. Вызов осуществляется функцией CommConfigDialog(), которая в качестве параметров принимает имя настраиваемого порта, хендл родительского окна и указатель на структуру COMMCONFIG. Следует отметить, что для корректного вызова окна, структура COMMCONFIG должна быть заполнена значениями заранее. Настройку структуры можно выполнить вручную или при помощи функции GetCommConfig(). Например:

/* Получение существующих настроек */
unsigned long new_size = 0;
if (!GetCommConfig(port->handle, &port->settings,&new_size))
	goto error;
/* Вызов окна и установка новых параметров */
if (CommConfigDialog(port->name, 0, &port->settings)) {
	if (SetCommConfig(port->handle, &port->settings, port->settings.dwSize))
		return 1;
	goto error;
}

Выделение памяти для структуры COMMPORT[править]

Прием и передача данных[править]

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

CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

Предпоследний параметр dwFlagsAndAttributes должен быть равен 0. После успешного открытия порта, данные могут быть считаны или записаны при помощи функций ReadFile() и WriteFile().

HANDLE port = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,
			OPEN_EXISTING, 0, NULL);
unsigned char dst[1024] = {0};

unsigned long size = sizeof(dst);
if(port!= INVALID_HANDLE_VALUE) 
	if(ReadFile(port,dst,size, &size,0))
		printf("\nRead %d bytes",size);

Функция ReadFile/WriteFile осуществляет чтение/запись из файла (устройства) начиная с текущей позиции после окончания чтения обновляет указатель в файле.

BOOL ReadFile(
 HANDLE hFile, // хендл файла 
 LPVOID lpBuffer, //указатель на буфер 
 DWORD nNumberOfBytesToRead, // размер данных 
 LPDWORD lpNumberOfBytesRead, //размер считанных данных
 LPOVERLAPPED lpOverlapped //структура OVERLAPPED
);

Недостатком этого способа является то, что вызывая функцию ReadFile(), мы не знаем есть ли данные для чтения. Можно циклически проверять их наличие, но это приводит к дополнительным расходам времени ЦП. Поэтому на практике часто удобней использовать асинхронный режим. Для этого при вызове функции CreateFile() параметр dwFlagsAndAttributes должен быть равен FILE_FLAG_OVERLAPPED.

CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,
		OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

Далее, необходимо настроить реакцию порта на события при помощи функции SetCommMask() и используя функции WaitCommEvent() и WaitForSingleObject() ожидать событие или тайм аут. Например:

const int READ_TIME = 100;
OVERLAPPED sync = {0};
int reuslt = 0;
unsigned long wait = 0, read = 0, state = 0;
	
/* Создаем объект синхронизации */
sync.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);

/* Устанавливаем маску на события порта */
if(SetCommMask(port, EV_RXCHAR)) {
	/* Связываем порт и объект синхронизации*/
	WaitCommEvent(port, &state, &sync);
	/* Начинаем ожидание данных*/	
	wait = WaitForSingleObject(sync.hEvent, READ_TIME);
	/* Данные получены */		
	if(wait == WAIT_OBJECT_0) {
		/* Начинаем чтение данных */
		ReadFile(port, dst, size, &read, &sync);
		/* Ждем завершения операции чтения */
		wait = WaitForSingleObject(sync.hEvent, READ_TIME);
		/* Если все успешно завершено, узнаем какой объем данных прочитан */
		if(wait == WAIT_OBJECT_0) 
			if(GetOverlappedResult(port, &sync, &read, FALSE)) 
				reuslt = read;
	}
}
CloseHandle(sync.hEvent);

Сброс порта[править]

Пример настройки порта и выполнения чтения/записи данных[править]

Код для работы с COM-портом. Многострадальный, соответственно относительно простой и понятный, при этом обходит основные подводные камни. Надеюсь, может быть полезен.

tty.h[править]

 #ifndef TTY_H
 #define TTY_H

 #define NOMINMAX //иначе API windows определит макросы min и max, конфликтующие с std::max и std::min в vector
 #include <windows.h>

 #include <vector>
 #include <string>

 using namespace std;

 struct TTY {

 	TTY();
 	virtual ~TTY();

 	bool IsOK() const;
	
 	void Connect(const string& port, int baudrate);
 	void Disconnect();

 	virtual void Write(const vector<unsigned char>& data);
 	virtual void Read(vector<unsigned char>& data);
	
 	HANDLE m_Handle;

 };

 struct TTYException {
 };

 #endif

tty.cpp[править]

 #include "tty.h"
#include <iostream>
#include <assert.h>
#include <windows.h>

  using namespace std;

 static int TIMEOUT = 1000;

 TTY::TTY() {
 	m_Handle = INVALID_HANDLE_VALUE;
 }

 TTY::~TTY() {
 	Disconnect();
 }

  bool TTY::IsOK() const {
      return m_Handle != INVALID_HANDLE_VALUE;
  }

 void TTY::Connect(const string& port, int baudrate) {

 	Disconnect();
	
 	m_Handle =
 		CreateFile(
 		port.c_str(), 
 		GENERIC_READ | GENERIC_WRITE,
 		0,
 		NULL,
 		OPEN_EXISTING, 
 		FILE_ATTRIBUTE_NORMAL,
 		NULL);
	
 	if(m_Handle == INVALID_HANDLE_VALUE) {
 		throw TTYException();
 	}
	
 	SetCommMask(m_Handle, EV_RXCHAR);
 	SetupComm(m_Handle, 1500, 1500);

 	COMMTIMEOUTS CommTimeOuts;
 	CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
 	CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
 	CommTimeOuts.ReadTotalTimeoutConstant = TIMEOUT;
 	CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
 	CommTimeOuts.WriteTotalTimeoutConstant = TIMEOUT;

 	if(!SetCommTimeouts(m_Handle, &CommTimeOuts)) {
 		CloseHandle(m_Handle);
                m_Handle = INVALID_HANDLE_VALUE;
 		throw TTYException();
 	}
	
 	DCB ComDCM;
	
 	memset(&ComDCM,0,sizeof(ComDCM));
 	ComDCM.DCBlength = sizeof(DCB);
 	GetCommState(m_Handle, &ComDCM);
 	ComDCM.BaudRate = DWORD(baudrate);
 	ComDCM.ByteSize = 8;
 	ComDCM.Parity = NOPARITY;
 	ComDCM.StopBits = ONESTOPBIT;
 	ComDCM.fAbortOnError = TRUE;
 	ComDCM.fDtrControl = DTR_CONTROL_DISABLE;
 	ComDCM.fRtsControl = RTS_CONTROL_DISABLE;
 	ComDCM.fBinary = TRUE;
 	ComDCM.fParity = FALSE;
 	ComDCM.fInX = FALSE;
        ComDCM.fOutX = FALSE;
 	ComDCM.XonChar = 0;
 	ComDCM.XoffChar = (unsigned char)0xFF;
 	ComDCM.fErrorChar = FALSE;
 	ComDCM.fNull = FALSE;
 	ComDCM.fOutxCtsFlow = FALSE;
 	ComDCM.fOutxDsrFlow = FALSE;
 	ComDCM.XonLim = 128;
 	ComDCM.XoffLim = 128;

 	if(!SetCommState(m_Handle, &ComDCM)) {
 		CloseHandle(m_Handle);
 		m_Handle = INVALID_HANDLE_VALUE;
 		throw TTYException();
 	}

 }

 void TTY::Disconnect() {

 	if(m_Handle != INVALID_HANDLE_VALUE)
        {
 	   CloseHandle(m_Handle);
           m_Handle = INVALID_HANDLE_VALUE;
 	}

 }

 void TTY::Write(const vector<unsigned char>& data) {

 	if(m_Handle == INVALID_HANDLE_VALUE) {
 		throw TTYException();
 	}

 	DWORD feedback;
 	if(!WriteFile(m_Handle, &data[0], (DWORD)data.size(), &feedback, 0) || feedback != (DWORD)data.size()) {
 		CloseHandle(m_Handle);
 		m_Handle = INVALID_HANDLE_VALUE;
 		throw TTYException();
 	}

 	// In some cases it's worth uncommenting
 	//FlushFileBuffers(m_Handle);

 }

 void TTY::Read(vector<unsigned char>& data) {

 	if(m_Handle == INVALID_HANDLE_VALUE) {
 		throw TTYException();
 	}

 	DWORD begin = GetTickCount();
 	DWORD feedback = 0;

 	unsigned char* buf = &data[0];
 	DWORD len = (DWORD)data.size();
	
 	int attempts = 3;
 	while(len && (attempts || (GetTickCount()-begin) < (DWORD)TIMEOUT/3)) {

 		if(attempts) attempts--;

 		if(!ReadFile(m_Handle, buf, len, &feedback, NULL)) {
 			CloseHandle(m_Handle);
 			m_Handle = INVALID_HANDLE_VALUE;
 			throw TTYException();
 		}

 		assert(feedback <= len);
 		len -= feedback;
 		buf += feedback;
	
 	}

 	if(len) {
 		CloseHandle(m_Handle);
 		m_Handle = INVALID_HANDLE_VALUE;
 		throw TTYException();
 	}

 }

using namespace std;

int main(int argc, char *argv[])
{
    TTY tty;
    tty.Connect("COM4",9600);
    
    for(int i=0;i<1000;i++) {
            
        std::vector<unsigned char> the_vectsor;
        the_vectsor.push_back(5);
        tty.Read(the_vectsor);
    
                              
        std::cout << (char)(the_vectsor[0]); //output text
    
    }
    
    system("PAUSE");
    return EXIT_SUCCESS;
}