Данная статья посвящена теме программирования последовательного порта ПК.
Данный интерфейс до сих пор очень широко используется, начиная от подключения мышки к
компу и заканчивая мощнейшими приборами, которые взаимодействуют с компутером.
Данный интерфейс позволяет производить ввод/вывод данных, очень гибкую настройку режима
передачи, и хороший контроль состояния порта и ошибок, которые возникают. Поэтому то, как
и для чего вы будете использовать данный интерфейс зависит большей частью от вашей фантазии,
мне, к примеру, доводилось писать много разных тулз, которые работали с данным интерфейсом.
Это: программа для управление ПК посредством инфракрасного сигнала, передаваемого с пульта
ДУ на комп. К компу подключается простенькая схемка с инфракрасным приемником и программа
уже обрабатывает сигналы, которые поступают на порт с прибора. Также разные тулзы для обработки
сигналов с разных приборов.
Что еще интеретного бывает, так это чаты, которые работают через com-порты компутеров,
соединенных между собой. В общем кто как может так и пользует этот интерфейс
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
ps модемы, кстати, тоже на этот порт вешаются, так что разобравшись с командами вашего
модема вы сможете закодить свою програму для работы с модемом
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
*** Немного об интерфейсе RS-232 ***
Интерфейс RS-232-C разработан ассоциацией электронной промышленности (Electronic
Industries Association - EIA) как стандарт для соединения компьютеров и различных
последовательных периферийных устройств.
Данный интерфейс является асинхронным. Тоесть позволяет одновременное выполнение нескольких
операция ввода/вывода. Соответственно средства кодинга порта учитывают это и работа с портом
является оч удобной и логичной.
В основе порта лежит микросхема Intel 8250 или ее современные аналоги -
Intel 16450, 16550, 16550A. Эта микросхема является универсальным асинхронным
приемопередатчиком (UART - Universal Asynchronous Receiver Transmitter).
В данной микрухе имеются сдвиговый и буферный регистры приемника.
Программа имеет доступ только к буферным регистрам, копирование информации в
сдвиговые регистры и процесс сдвига выполняется микросхемой UART автоматически.
*** Низкоуровневый доступ к портам. Функции BIOS ***
те, кто кодируют на асме знают что взаимодействие с различными аппаратным средствам
ПК осуществляется посредством вызова соответствующего прерывания или функции BIOS.
Так вот, наш интерфейс RS-232 (com-порт) тоже может вырабатывать прерывания:
COM1, COM3 - IRQ4 (соответствует INT 0Ch)
COM2, COM4 - IRQ3 (соответствует INT 0Bh)
COM1 имеет базовый адрес 3F8h и занимает диапазон адресов от 3F8h до 3FFh
COM2 имеет базовый адрес 2F8h и занимает адреса 2F8h...2FFh
COM3 имеет базовый адрес 3E8h и занимает диапазон адресов от 3E8h до 3EFh
COM4 имеет базовый адрес 2E8h и занимает адреса 2E8h...2EFh
Рассмотрим функции, которые нам доступны для работы с портом:
AH=00h - Инициализация порта
AL: смотрите в таблице
DX: номер порта(0-3; 0 equ. 0x3f8, 1 equ. 0x2f8, и т.д.)
Bit 7 Bit 6 Bit 5 Rate [bps] Bit 4 Bit 3 Parity
1 1 1 9600 0 0 none
1 1 0 4800 1 0 none
1 0 1 2400 0 1 odd
1 0 0 1200 1 1 even
0 1 1 600
0 1 0 300 Bit 1 Bit 0 Data bits
0 0 1 150 0 0 5
0 0 0 110 0 1 6
1 0 7
Bit 2 0 -> 1 stop bit, 1 -> 2 stop bits 1 1 8
(!)Возвращаемые значения:
AH: RS-232C бит статуса линии
Бит
0: RBF - данные доступны в буфере
1: OE - данные утеряны
5: THRE - room is available in output buffer
6: TEMT - буфер пустой
AL: биты статуса модема
3: всегда 1
7: DCD - несущая
AH=01h - Записать байт
AL: символ для посылки в порт
DX: порт
(!)Возвращаемые значения:
AH: 7-й бит сброшен - все ок, установлен - возникла ошибка.
Биты 0-6 смотрите INT 14h AH=03h
AH=02h - Прочитать байт
(!)Возвращаемые значения:
AH: Состояние линии (смотрите AH=03h)
AL: принятый символ (если 7-й бит AH не установлен)
AH=03h - получить информацию о статусе порта
DX: Порт
(!)Возвращаемые значения:
AH: статус линии
Bit 7: Timeout
Bit 6: TEMT Transmitter empty
Bit 5: THRE Transmitter Holding Register Empty
Bit 4: Break (broken line detected)
Bit 3: FE Framing error
Bit 2: PE Parity error
Bit 1: OE Overrun error
Bit 0: RDF Receiver buffer full (data available)
AL: Modem Status
Bit 7: DCD Carrier detect
Bit 6: RI Ring indicator
Bit 5: DSR Data set ready
Bit 4: CTS Clear to send
Bit 3: DDCD Delta carrier detect
Bit 2: TERI Trailing edge of ring indicator
Bit 1: DDSR Delta data set ready
Bit 0: DCTS Delta Clear to send
*** структуры для работы с com-портом, доступные на уровне win32API ***
win32api предоставляют нам пять структур для работы с com-портом.
структуры для работы с com-портом объявлены в заголовочном файле winbase.h
COMMCONFIG - предоставляет информацию о конфигурации комуникационного устройства
члены структуры:
DWORD dwSize; /* Size of the entire struct */
WORD wVersion; /* version of the structure */
WORD wReserved; /* alignment */
DCB dcb; /* device control block */
DWORD dwProviderSubType; /* ordinal value for identifying provider-defined data structure format*/
DWORD dwProviderOffset; /* Specifies the offset of provider specific data field in bytes from the start */
DWORD dwProviderSize; /* size of the provider-specific data field */
WCHAR wcProviderData[1]; /* provider-specific data */
COMMPROP - структура, заполняемая при вызове GetCommProperties (), предоставляет информацию
о комуникацонном драйвере
члены структуры:
WORD wPacketLength;
WORD wPacketVersion;
DWORD dwServiceMask;
DWORD dwReserved1;
DWORD dwMaxTxQueue;
DWORD dwMaxRxQueue;
DWORD dwMaxBaud;
DWORD dwProvSubType;
DWORD dwProvCapabilities;
DWORD dwSettableParams;
DWORD dwSettableBaud;
WORD wSettableData;
WORD wSettableStopParity;
DWORD dwCurrentTxQueue;
DWORD dwCurrentRxQueue;
DWORD dwProvSpec1;
DWORD dwProvSpec2;
WCHAR wcProvChar[1];
COMMTIMEOUTS - определяет значения таймаутов для операций ввода/вывода. используются функции
SetCommTimeouts() и GetCommTimeouts()
члены структуры:
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. */
COMSTAT - возвращает информацию о текущем состоянии последовательного порта, заполняется
при вызове функции ClearCommError()
члены структуры:
DWORD fCtsHold : 1;
DWORD fDsrHold : 1;
DWORD fRlsdHold : 1;
DWORD fXoffHold : 1;
DWORD fXoffSent : 1;
DWORD fEof : 1;
DWORD fTxim : 1;
DWORD fReserved : 25;
DWORD cbInQue;
DWORD cbOutQue;
DCB - содержит основные установки комуникационного устройства. для получения структуры используйте
функцию GetCommState()
члены структуры:
DWORD DCBlength; /* sizeof(DCB) */
DWORD BaudRate; /* Baudrate at which running */
DWORD fBinary: 1; /* Binary Mode (skip EOF check) */
DWORD fParity: 1; /* Enable parity checking */
DWORD fOutxCtsFlow:1; /* CTS handshaking on output */
DWORD fOutxDsrFlow:1; /* DSR handshaking on output */
DWORD fDtrControl:2; /* DTR Flow control */
DWORD fDsrSensitivity:1; /* DSR Sensitivity */
DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */
DWORD fOutX: 1; /* Enable output X-ON/X-OFF */
DWORD fInX: 1; /* Enable input X-ON/X-OFF */
DWORD fErrorChar: 1; /* Enable Err Replacement */
DWORD fNull: 1; /* Enable Null stripping */
DWORD fRtsControl:2; /* Rts Flow control */
DWORD fAbortOnError:1; /* Abort all reads and writes on Error */
DWORD fDummy2:17; /* Reserved */
WORD wReserved; /* Not currently used */
WORD XonLim; /* Transmit X-ON threshold */
WORD XoffLim; /* Transmit X-OFF threshold */
BYTE ByteSize; /* Number of bits/byte, 4-8 */
BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */
BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */
char XonChar; /* Tx and Rx X-ON character */
char XoffChar; /* Tx and Rx X-OFF character */
char ErrorChar; /* Error replacement char */
char EofChar; /* End of Input character */
char EvtChar; /* Received Event character */
WORD wReserved1; /* Fill for now. */
ну и можно добавить две структуры для работы с модемами, которые объявлены в mcx.h:
MODEMDEVCAPS - структура содержит информацию об аппаратных возможностях модема
члены структуры:
DWORD dwActualSize;
DWORD dwRequiredSize;
DWORD dwDevSpecificOffset;
DWORD dwDevSpecificSize;
// product and version identification
DWORD dwModemProviderVersion;
DWORD dwModemManufacturerOffset;
DWORD dwModemManufacturerSize;
DWORD dwModemModelOffset;
DWORD dwModemModelSize;
DWORD dwModemVersionOffset;
DWORD dwModemVersionSize;
// local option capabilities
DWORD dwDialOptions; // bitmap of supported values
DWORD dwCallSetupFailTimer; // maximum in seconds
DWORD dwInactivityTimeout; // maximum in seconds
DWORD dwSpeakerVolume; // bitmap of supported values
DWORD dwSpeakerMode; // bitmap of supported values
DWORD dwModemOptions; // bitmap of supported values
DWORD dwMaxDTERate; // maximum value in bit/s
DWORD dwMaxDCERate; // maximum value in bit/s
// Variable portion for proprietary expansion
BYTE abVariablePortion [1];
MODEMSETTINGS - информация об настройках и установках модема
члены структуры:
DWORD dwActualSize;
DWORD dwRequiredSize;
DWORD dwDevSpecificOffset;
DWORD dwDevSpecificSize;
// static local options (read/write)
DWORD dwCallSetupFailTimer; // seconds
DWORD dwInactivityTimeout; // seconds
DWORD dwSpeakerVolume; // level
DWORD dwSpeakerMode; // mode
DWORD dwPreferredModemOptions; // bitmap
// negotiated options (read only) for current or last call
DWORD dwNegotiatedModemOptions; // bitmap
DWORD dwNegotiatedDCERate; // bit/s
// Variable portion for proprietary expansion
BYTE abVariablePortion [1];
*** win32API функции для работы с com-портом ***
win32api предоставляет множество функций для работы с устройствами связи и последовательными портами ввода вывода.
благодарю такому набору средств можно писать очень эфективные програмы для работы с портами, етц.
далее я приведу перечень все функция с описание функции, также укажу параметры функции и их типы. более детальную информацию
о параметрах функций и самих функциях вы можете найти в MSDN'е (
http://msdn.microsoft.com) или же в других источниках
информации. если я стану описывать все нюансы касательно каждой функции и ее аргумента - статья превратится в настоящую книгу
объемом порядка сотни страниц. да и имена параметров достаточно понятны чтобы описывать каждый отдельно
![Подмигиваю ;)](https://guitarplayer.ru/Smileys/classic/wink.gif)
BuildCommDCB - Заполняет указанную структуру блока описания устройств значениями, указанными в строке управления устройства.
BOOL BuildCommDCB(
LPCTSTR lpDef,
LPDCB lpDCB
);
BuildCommDCBAndTimeouts - Переводит строку определения устройства в соответствующие коды управляющего блока устройства и
размещает их в управляющий блок устройства.
BOOL BuildCommDCBAndTimeouts(
LPCTSTR lpDef,
LPDCB lpDCB,
LPCOMMTIMEOUTS lpCommTimeouts
);
ClearCommBreak - Передача символа восстановлений для указанного устройства связи. Сбрасывает значения ошибок и заполняет
структуру COMSTAT.
BOOL ClearCommBreak(
HANDLE hFile
);
ClearCommError - Вовращает информацию об ошибках связи и сообщает о текущем состоянии устройства связи.
BOOL ClearCommError(
HANDLE hFile,
LPDWORD lpErrors,
LPCOMSTAT lpStat
);
CommConfigDialog - Отображает снабженное драйвером диалоговое окно конфигурации.
BOOL CommConfigDialog(
LPCTSTR lpszName,
HWND hWnd,
LPCOMMCONFIG lpCC
);
EscapeCommFunction - Указывает указанному устройство связи, что необходимо выполнить расширенную функцию.
BOOL EscapeCommFunction(
HANDLE hFile,
DWORD dwFunc
);
GetCommConfig - Возвращает текущую конфигурацию устройства связи
BOOL GetCommConfig(
HANDLE hCommDev,
LPCOMMCONFIG lpCC,
LPDWORD lpdwSize
);
GetCommMask - Возвращает значение маски события для указанного устройства связи.
BOOL GetCommMask(
HANDLE hFile,
LPDWORD lpEvtMask
);
GetCommModemStatus - Возвращает модемные значения регистра управления.
BOOL GetCommModemStatus(
HANDLE hFile,
LPDWORD lpModemStat
);
GetCommProperties - Возвращает информацию о свойствах связи для указанного устройства связи.
BOOL GetCommProperties(
HANDLE hFile,
LPCOMMPROP lpCommProp
);
GetCommState - Возвращает текущие параметры настройки управления для указанного устройства связи.
BOOL GetCommState(
HANDLE hFile,
LPDCB lpDCB
);
GetCommTimeout - Возвращает параметры блокировки времени для всего чтения и операций записи на указанном устройстве связи.
BOOL GetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
GetDefaultCommConfig - Возвращает заданную по умолчанию конфигурацию для указанного устройства связи.
BOOL GetDefaultCommConfig(
LPCTSTR lpszName,
LPCOMMCONFIG lpCC,
LPDWORD lpdwSize
);
PurgeComm - Сбрасывает все символы вывода или входного буфера указанного ресурса связи.
BOOL PurgeComm(
HANDLE hFile,
DWORD dwFlags
);
SetCommBreak - Передача символа Suspends для указанного устройства связи и установка состояния break до вызова ф-ции
ClearCommBreak()
BOOL SetCommBreak(
HANDLE hFile
);
SetCommConfig - Устанавливает текущую конфигурацию устройства связи.
BOOL SetCommConfig(
HANDLE hCommDev,
LPCOMMCONFIG lpCC,
DWORD dwSize
);
SetCommMask - Определяет набор событий, которые будут зафиксированы для устройства связи.
BOOL SetCommMask(
HANDLE hFile,
DWORD dwEvtMask
);
SetCommState - Конфигурирует устройство связи согласно спецификациям в управляющем блоке устройства.
BOOL SetCommState(
HANDLE hFile,
LPDCB lpDCB
);
SetCommTimeouts - Устанавливает параметры блокировки времени для всего чтения и операций записи на указанном
устройстве связи.
BOOL SetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
SetDefaultCommConfig - Устанавливает заданную по умолчанию конфигурацию для устройства связи.
BOOL SetDefaultCommConfig(
LPCTSTR lpszName,
LPCOMMCONFIG lpCC,
DWORD dwSize
);
SetupComm - Инициализирует параметры связи для указанного устройства связи.
BOOL SetupComm(
HANDLE hFile,
DWORD dwInQueue,
DWORD dwOutQueue
);
TransmitCommChar - Передает указанный символ перед любыми операциями ожидания данных в буфере вывода указанного
устройства связи.
BOOL TransmitCommChar(
HANDLE hFile,
char cChar
);
WaitCommEvent - Ожидает события, которые установлены с помощью SetCommMask()
BOOL WaitCommEvent(
HANDLE hFile,
LPDWORD lpEvtMask,
LPOVERLAPPED lpOverlapped
);
*** тонкости работы с последовательным com-портом ***
ну вот теперь вы в курсе что такое com-порт и какими средствами мы владеем для программирования
данного интерфейса. начнем мы с простого примера и одновременно самой распространенной
операции - чтение данных из порта. итак, приступим.
как мы уже сказали, при работе с портом на ассемблере мы используем соответствующие функции
BIOS, выше я уже перечислил их.
для чтения и записи в порт в асме есть две простые команды:
in аккумулятор, номер_порта ; ввод аккумулятора из порта с номером номер_порта
out порт, аккумулятор ; вывод содержимого аккумулятора в порт с номером номер_порта
тут все просто как двери. загнали в регистры нужные значения, вызвали функцию, потом прерывание
и вперед - анализируем значения, которые устанавливаются после выполнения определенной функции.
это все было описано выше. так как основное внимание в данной статье уделено кодингу с
использованием win32api функций - на асме останавливаться не будем, так как на нем это
все делается гораздо проще нежели многие думают, там нет пары десятков функций и структур,
асм прост, как говорят - все гениальное просто. так и асм. ну да ладно, если кто считает,
что кодинг на асме это изврат пускай и дальше заблуждается и верит в свое ограничивающее
убеждение
![Подмигиваю ;)](https://guitarplayer.ru/Smileys/classic/wink.gif)
)
итак. с асмом все понятно
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
а как быть с api функциями?
с ними тоже все просто, нужно одинраз разобраться и все станет понятно.
для начала необходимо проинициализировать порт, для этого используется функция CreateFile()
char *port_ptr = "COM1";
HANDLE hCom = CreateFile(port_ptr, // init port
GENERIC_READ | GENERIC_WRITE, // read/write access
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,// default security attribute
OPEN_EXISTING, //COM's must exist 8-)
FILE_FLAG_OVERLAPPED, //use for overlapped i/o.
0
);
единственное что стоит сказать относителньо использования этой функции для инициализации
порта - не игнорируйте шестой параметр и устанавливайте его FILE_FLAG_OVERLAPPED. это укажет
функции, что необходимо инициализировать порт в режиме асинхронного ввода/вывода.
что это значит? к примеру вы написали програмку, которая читает данные из порта, анализирует
их. что-то еще делает, с окошками, менюшками, разными фичами. так вот. если работать
с портом в режиме синхронного ввода/вывода - поток, в котором происходит обращение к ресурсам
порта блокируется до появления событий порта, а в режиме асинхронного вв/в - операция ожидания
переводится в фоновый режим выплнения и вы можете в том же потоке делать что вам необходимо
тем самым не мешая программе ожидать событий от порта...
// итак, инициализируем порт, проверяем результат выполнения функции:
if( hCom != INVALID_HANDLE_VALUE )
printf("(+) %s initialized\n", port_ptr);
else
printf("(-) %s initialization failed\n", port_ptr);
// теперь нужно сбросить значения буферов порта и регистров:
int retcode
=
PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
// retcode будет содержать код возврата
if( retcode==NULL )
printf("(-) PurgeComm() failed\n");
else
printf("(+) Discarded all characters from the output and input buffer\n");
retcode = ClearCommBreak(hCom);
if( retcode==NULL ){
printf("(-) ClearCommBreak() failed\n");
PrintLastError();
}else
printf("(+) Restored character transmission and placed the transmission line in a nonbreak state\n");
// далее подготовим структуру DCB для того чтобы внести некоторые изменения в параметрах порта:
DCB dcb;
retcode = GetCommState(hCom,&dcb);
if( retcode==NULL )
printf("(-) GetCommState() failed\n");
else
printf("(+) Retrieved the current control settings for %s\n", port_ptr);
// изменяем только то что нам нужно:
dcb.BaudRate = CBR_9600; // set the baud rate
dcb.ByteSize = 8; // data size, xmit, and rcv
dcb.Parity = NOPARITY; // no parity bit
dcb.StopBits = ONESTOPBIT; // one stop bit
// и устанавливаем новый опции порта:
retcode = SetCommState(hCom, &dcb);
// и опять проверяем код возврата, на всякий случай... надеюсь я вас научу это всегда делать,
// это является правилом хорошего тона. с таким подходом никогда не затеряетесь в своем коде
// и легконайдете причину возникновения ошибок, если таковые будут
if( retcode==NULL )
printf("(-) SetCommState() failed\n");
else
printf("(+) Reinitialized all hardware and control settings \n"\
// устанавливаем события, о появлении которых мы хотим узнать:
// (будем мониторить все события)
DWORD CommEventMask = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |
EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY;
retcode = SetCommMask(hCom,CommEventMask);
if( retcode==NULL ){
printf("(-) SetCommMask() failed\n");
PrintLastError();
}else
printf("(+) Events to be monitored -> BREAK, CTS, DSR, ERR, RING, RLSD, RXCHAR, RXFLAG, TXEMPTY\n");
// теперь создадим event, который нам сообшит о проишедшем событии:
OVERLAPPED OL;
// эта структура требуется в многих функциях при асинхронном вв/в
// как не сложно заметить, она содержит в себе параметр типа HANDLE, который и будет нашим
// event'ом
OL.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD EventMask = 0; //сюда будет записан тип события
do{
// обрабатываем события в цикле, дабы программа не закончилась после обработки одного события
// если этого непосредственно не нужно
retcode = WaitCommEvent(hCom, &EventMask, &OL);
if ( ( !retcode ) && (GetLastError()==ERROR_IO_PENDING) ){
printf("(!) Waiting for event\n");
WaitForSingleObject(OL.hEvent, INFINITE);
}
// поясняю этот код:
// WaitCommEvent() ожидает событий от com-порта, тип событий ранее мы "привязали" к хэндлу порта
// с помощью SetCommEvent()
// retcode сообщает о результате выполнения функции. retcode должен быть NULL и
// GetLastError() должна возвратать нам 997 (ERROR_IO_PENDING), что означает
// "Overlapped I/O operation is in progress", тоесть функция успешно выполнилась и перевела
// операцию ожидания событий в фоновый процесс, о чем мы и говорили раньше - при асинхронном
// вв/в операции с портом не блокируют поток, в котором они выполняются.
// далее при помощи WaitForSingleObject() ожидаем события, INFINITE - это константа, которая
// задает интервал времени для ожидания в милисекундах, в даном случае INFINITE привыкли
// считать как вечное ожидание, на самом деле это не так, это максимальное 32-х разрядное
// число 0xFFFFFFFF, если подсчитать получается интервал ожидания больше чем год, думаю вам этого хватит
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
)
// тоесть задав вместо INFINITE 1000 вы указываете ждать 1сек
// далее анализируем чтоже произошло:
if(EventMask & EV_BREAK) printf("(i) EV_BREAK\n");
if(EventMask & EV_RLSD) printf("(i) EV_RLSD\n");
if(EventMask & EV_CTS) printf("(i) EV_CTS\n");
if(EventMask & EV_DSR) printf("(i) EV_DSR\n");
if(EventMask & EV_ERR) printf("(i) EV_ERR\n");
if(EventMask & EV_RING) printf("(i) EV_RING\n");
if(EventMask & EV_RXCHAR) printf("(i) EV_RXCHAR\n");
if(EventMask & EV_RXFLAG) printf("(i) EV_RXFLAG\n");
if(EventMask & EV_TXEMPTY) printf("(i) EV_TXEMPTY\n");
// надеюсь тут все понятно, поразрядным & мы проверяем что у нас в EventMask
// ну и далее в зависимости от события вы работаете с портом дальше, рассмотрим случай с
// чтением данных из порта. в таком случае должно возникнуть событие EV_RXCHAR
// итак, если у нас возникает EV_RXCHAR - в буфере порта нас ожидает сюрприз, а именно
// какие-то данные. как же их поглядеть, а?
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
как как - считать в память и вывести на stdout
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
// для начала определим размер входящих данных. для этого нужно поглядеть на член структуры
// COMSTAT - cbInQue
// выше я писал, что для загрузки значений в COMSTAT пользуется функция ClearCommError()
DWORD ErrorMask = 0; // сюда будет занесен код ошибки порта, если таковая была
COMSTAT CStat;
ClearCommError(hCom, &ErrorMask, &CStat);
DWORD quelen = CStat.cbInQue;
// все. тепереь quelen содержит количество байт в буфере порта.
// выделяем память под буфер
char *lpInBuffer = new char[ (int)quelen+1 ];
memset(&lpInBuffer, '\0', (int)quelen);
DWORD dwReaded = 0;
// эта переменная после вызова ReadFile будет содержать количество число, которое покажет
// сколько байт было реально прочитано. как вы уже догадались, анализируя это значение можно
// судить об успешности выполнения операции чтения. если у нас в буфере было 512байт, а
// прочитали 0 или того меньше - чето не так, GetLastError() вам подскажет то именно
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
)
retcode = ReadFile(com->hCom, lpInBuffer, quelen, &dwReaded, &OL);
// опять же передаем структуру OL
if( dwReaded == 0 && GetLastError() == ERROR_IO_PENDING ) {
// если ничего не прочитано и GetLastError() возвратила ERROR_IO_PENDING
// операция не успела за установленный в COMMTIMEOUTS
// период времени выполниться и выполняется в фоне, тоесть вы можете спокойно чето делать
// в этом болке кода а после проанализировать результат выполнения
// DoSomeThing() //че-то делаем... а потом проверяем состояние
retcode = GetOverlappedResult(hCom, &OL, &dwreaded, FALSE) ;
// как вы уже догадались - dwreaded содержит кол-во прочитаных байт
}else{ //если что-то прочитали или возникла ошибка
if (dwReaded>0) //если прочитали данные
printf("(+) %d bytes received (%s)\n", dwReaded, lpInBuffer);
// по выводу вы увидете сколько байт прочитали и что именно
else
printf("(-) ReadFile() failed. errno %d\n", GetLastError());
}
// теперь нужно сбросить значения буферов порта и регистров:
if( PurgeComm(com->hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR) == NULL ){
printf("(-) PurgeComm() failed\n");
PrintLastError();
}else
printf("(+) Discarded all characters from the output and input buffer\n");
EventMask=0;
ResetEvent(OL.hEvent);
// сбрасываем значения нашего event'а перед ожиданием следующего события
//
}
}while(1);
выше могли заметить некую функцию PrintLastError(), это моя функция для вывода сообщения о последней
ошибке, вот вам в качестве бонуса
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
void PrintLastError(void){
int ercode = GetLastError();
printf("(-) GetLastError() return %i ", ercode );
switch( ercode ){
case 2: printf("(The system cannot find the file specified)\n"); break;
case 5: printf("(Access is denied)\n"); break;
case 29: printf("(The system cannot write to the specified device)\n"); break;
case 87: printf("(The parameter is incorrect)\n"); break;
case 998: printf("(Invalid access to memory location)\n"); break;
// тут описания для самых распространенных ошибок, остальные пару сотен добавите сами
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
default: printf("\n");
}
}
ну а как писать в порт? вместо ReadFile() пользуйте WriteFile()
напоследок советую всем обзавестить MSDN, где все оч оч подробно описано
![Cool 8)](https://guitarplayer.ru/Smileys/classic/cool.gif)
главное, что необходимо запомнить - всегда проверяйте коды возврата и если вы работаете
в асинхронном режиме с портом - проверяйте на наличие ERROR_IO_PENDING, что будет свидетельствовать
о том что операция выполняется в фоне и не стоит преждевременно суетиться и искать ошибки, которых
нет