Функция SetCommTimeouts
Заполнив структуру COMMTIMEOUTS можно вызывать функцию установки тайм-аутов порта. Это функция называется SetCommTimeouts:
BOOL SetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
Параметры этой функции тоже достаточно очевидны. Установку тайм-аутов можно производить как до установки параметров порта, так и после. Последовательность вызова функций SetCommState и SetCommTimeouts не имеет никакого значения. Главное, что бы все настройки были завершены до начала ввода/вывода информации.
Теперь приведу пример настройки порта:
#include <windows.h>
. . .
DCB *dcb;
COMMTIMEOUTS ct;
HANDLE port;
. . .
dcb=(DCB*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DCB));
dcb->DCBlength=sizeof(DCB);
BuildCommDCB("baud=9600 parity=N data=8 stop=1",dcb);
dcb->fNull=TRUE;
ct.ReadIntervalTimeout=10;
ct.ReadTotalTimeoutMultiplier=ct.ReadTotalTimeoutConstant=0;
ct.WriteTotalTimeoutMultiplier=ct.WriteTotalTimeoutConstant=0;
port=CreateFile("COM2",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
SetCommState(port,dcb);
SetCommTimeouts(port,&ct);
HeapFree(GetProcessHeap(),0,dcb);
. . .
CloseHandle(port);
В этом примере полностью убрана обработка ошибок. Обрабатывать ошибки необходимо, но сейчас главное разобраться в работе с портом, а обработка ошибок уменьшает наглядность.
Первым делом, с помощью функции HeapAlloc, выделяется и заполняется нулями область памяти для DCB. Затем в поле DCBlength
заносится размер структуры DCB в байтах. Зачем это нужно обсуждалось выше, при описании данного поля. Для общего (и наглядного) заполнения DCB
использована функция BuildCommDCB. Будем считать, что нас устраивает информация занесеная в DCB, но требуется игнорировать нулевые байты при приеме. Так как BuildCommDCB не выполняет требуемых действий мы вручную изменяем соответсвующее поле. Далее мы заполняем информацию о тайм-аутах.
Общие тайм-ауты операций чтения и записи не используются, конец сообщения определяется по тайм-ауту между двумя последовательными символами большему 10 миллисекунд. Теперь можно открыть порт, что делается функцией CreateFile, и выполнить его настройку вызвав функции SetCommState и SetCommTimeots. После установки параметров порта структура DCB становится не нужной, поэтому можно освободить занимаемую ей память. Структура COMMTIMEOUTS в примере размещена статически, поэтому выделять под нее память и освобждать ее не требуется. Наконец, мы закрываем порт перед завершением.
Функции HeapAlloc и HeapFree занимаются выделением и освобождением памяти из куч, которых в программе может быть несколько. Вместо этих функций можно использовать malloc (calloc) и free. Однако использование функций предоставляемых Win32 API позволяет сократить размер программы, что может быть не маловажно, если работа с портами ведется из DLL (например Вы пишете своеобразный псевдодрайвер для своего устройства). Есть и другие аргументы в пользу этой точки зрения, которую я Вам, впрочем, не навязываю.
Рассмотренные структуры и функции позволяют программировать порт на достаточно низком уровне. Их, в большинстве случаев, более чем достаточно даже для тонкой настройки порта. Однако бывают и исключения. Например, под именем COM1 может скрываться вовсе не привычный порт RS-232, а какая-нибудь экзотика. Или порт может не позволять задавать скорость более 9600.