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
Файл будет и считываться и записываться.
править
Задает параметры совместного доступа к файлу. Коммуникационные порты нельзя делать разделяемыми, поэтому данный параметр должен быть равен 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;
}