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
);
При успешном завершении функция возвращает не нулевое значение, при ошибке нуль.
#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;
}