Интернет-журнал "Домашняя лаборатория", 2007 №8
Шрифт:
Синхронный режим обмена довольно редко оказывается подходящим для серьезной работы с внешними устройствами через последовательные порты. Вместо полезной работы Ваша программа будет ждать завершения ввода/вывода, ведь порты работают значительно медленнее процессора. Да и гораздо лучше отдать время процессора другой программе, чем крутиться в цикле, ожидая какого-либо события. Другими словами, пришло время знакомиться с асинхронной работой с портами.
Функция SetCommMask
Начнем с событий связанных с последовательными
BOOL SetCommMask(
HANDLE hFile,
DWORD dwEvtMask
);
Маска отслеживаемых событий задается вторым параметром. Можно указывать любую комбинацию следующих значений:
• EV_BREAK — Состояние разрыва приемной линии
• EV_CTS — Изменение состояния линии CTS
• EV_DSR — Изменение состояния линии DSR
• EV_ERR — Ошибка обрамления, перебега или четности
• EV_RING — Входящий звонок на модем (сигнал на линии RI порта)
• EV_RLSD — Изменение состояния линии RLSD (DCD)
• EV_RXCHAR — Символ принят и помещен в приемный буфер
• EV_RXFLAG — Принят символ заданный полем EvtChar структуры DCB использованной для на стройки режимов работы порта
• EV_TXEMPTY — Из буфера передачи передан последний символ
Если dwEvtMask равно нулю, то отслеживание событий запрещается.
Функция GetCommMask
Разумеется, всегда можно получить текущую маску отслеживаемых событий с помощью функции
BOOL GetCommMask(
HANDLE hFile,
LPDWORD IpEvtMask
);
Вторым параметром задается адрес переменной принимающей значение текущей установленной маски отслеживаемых событий. В дополнение к событиям, перечисленным в описании функции SetCommMask, данная функция может возвратить следующие:
• EV_EVENT1 — Устройство-зависимое событие
• EV_EVENT2 — Устройство-зависимое событие
• EV_PERR — Ошибка принтера
• EV_RX80FULL — Приемный буфер заполнен на 80 процентов
Эти дополнительные события используются внутри драйвера. Вы не должны переустанавливать состояние их отслеживания.
Функция WaitCommEvent
Когда маска отслеживаемых событий задана, Вы можете приостановить выполнение своей программы до наступления события. При этом программа не будет занимать процессор. Это выполняется вызовом функции
BOOL WaitCommEvent(
HANDLE hFile,
LPDWORD IpEvtMask,
LPOVERLAPPED lpOverlapped,
);
Замечу, что в переменной, адресуемой вторым параметром, не будут устанавливаться внутренние события драйвера (перечислены в описании функции GetCommMask). В единичное состояние установятся только те биты, которые соответствуют реально произошедшим событиям.
Адрес структуры OVERLAPPED требуется для асинхронного ожидания (возможно и такое). Однако пока будем полагать, что порт открыт для синхронных операций, следовательно этот параметр должен быть NULL. Замечу только, что при асинхронном ожидании данная функция может завершиться с ошибкой, если в процессе этого ожидания будет вызвана функция SetCommMask для переустановки маски событий. Кроме того, связанное со структурой OVERLAPPED
#include <windows.h>
…
DCB dcb;
COMMTIMEOUTS ct;
HANDLE port;
DWORD mask;
DWORD bc;
char buf[101];
…
dcb.DCBlength=sizeof(DCB);
BuildCommDCB("baud=9600 parity=N data=8 stop=1",&dcb);
dсb.fNull=TRUE;
ct.ReadIntervalTimeout=10;
ct.ReadTotalTimeoutMultiplier=ct.ReadTotalTimeoutConstant=0;
ct.WriteTotalTimeoutMultiplier=ct.WriteTotalTimeoutConstant=0;
port=CreateFile("COM2",GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
SetCommState(port,dcb);
SetCommTimeouts(port,&ct);
PurgeComm(port,PURGE_RXCLEAR);
…
SetCommMask(port,EV_RXCHAR);
WaitCommEvent(port,Smask,NULL);
ReadFile(port,buf,10 0,&bc,NULL);
CloseHandle(port);
…
В данном примере ожидается начало сообщения (первый полученный символ), после чего вызывается функция чтения. Освобождать процессор на время ожидания хорошо, но хотелось бы параллельно с вводом/выводом делать какую-либо полезную работу. Что бы это стало возможным, необходимо в качестве параметра dwFlagsAndAttributes вместо 0 указать FILE_FLAG_OVERLAPPED. Кроме того, для функций ReadFile, WriteFile и WaitCommEvent необходимо в качестве параметра lpOverlapped указывать адрес правильно инициализированной структуры OVERLAPPED.
Структура OVERLAPPED
Структура OVERLAPPED выглядит следующим образом:
typedef struct _OVERLAPPED {{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
Подробно описывать поля этой структуры не буду, поскольку данная статья не о файловом вводе/выводе вообще, а о работе с портами. Для наших целей, за исключением WaitCommEvent, можно просто обнулить все поля этой структуры. Для WaitCommEvent поле hEvent должно содержать корректный описатель объекта "событие". Что бы все стало понятно, надо разобраться с таким обязательным атрибутом параллельной работы как синхронизация.
Синхронизация нужна для упорядочения доступа к совместно используемым объектам. Предположим, что две программы одновременно пытаются изменить значение общей переменной. Каков будет результат? Скорее всего неопределенный. Что бы этого избежать требуется разрешать доступ второй программы к переменной только после того, как с ней закончила работать первая программа.
Для синхронизации используются различные методы: семафоры, блокировки, события, критические секции и т. п. События являются простейшими синхронизирующими объектами. Они могут находиться только в двух состояниях: установленном (событие произошло или наступило) и сброшенном (событие не произошло или не наступило). События создаются функцией CreateEvent и разрушаются функцией CloseHandle. Установить событие можно функцией SetEvent, а сбросить ResetEvent.