COM port with WIN32 API
От: Roman_M rgmroman.narod.ru
Дата: 28.06.01 07:59
Оценка:
Хотелось бы получить какие-нибудь полезные советы, подсказки и комментарии от тех кто уже освоил программирование COM портов на Win32 API. На данный момент я застрял на посылке данных в модем. Вот в кратце приведу код, который я написал:

ZeroMemory(&ov, sizeof(OVERLAPPED)); // Подготовка к асинхронным
ov.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); // операциям ввода-вывода

/* Открытие определенного порта */
hCom = CreateFile(szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);


fResult = SetCommState(hCom, &comdcb); // Инициализация порта

dwBaudRate = comdcb.BaudRate;

/* Очистка порта */
fResult = PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

fResult = SetupComm(hCom, 0x1000, 0x1000); // Установка размеров буферов I/O

cto.ReadIntervalTimeout = 0xFFFFFFFF; // Расчет и установка задержек. Идея взята из
cto.ReadTotalTimeoutConstant = 1000; // примеров в SDK. Вот тут хотелось бы
cto.WriteTotalTimeoutMultiplier = 2*CBR_9600/dwBaudRate; // каких-нибудь пояснений
fResult = SetCommTimeouts(hCom, &cto); //

fResult = SetCommMask(hCom, EV_RXCHAR); // Ставится маска событий

/* Запись строки инициализации. Я использую ATZ\r */
dwTemp = 0;
ResetEvent(ov.hEvent);
fResult = WriteFile(hCom, InitString, lstrlenA(InitString), &dwTemp, &ov);
if (fResult == FALSE)
{
array[0] = hExit;
array[1] = ov.hEvent;
dwTemp = WaitForMultipleObjects(2, array, FALSE, INFINITE);
if (dwTemp == WAIT_OBJECT_0)
{
CloseHandle(hCom);
/* здесь выход из цикла */
}
dwTemp = 0;
fResult = GetOverlappedResult(hCom, &ov, &dwTemp, TRUE);
if (fResult == FALSE)
{
/* Закрытие описателя, сообщение об ошибке, выход*/
}
}

// !!! Вот тут то у меня почему то часто dwTemp!=4, что видно по эхо выводу
// А Portmon'е может быть статус TIMEOUT.


array[0] = hExit;
array[1] = ov.hEvent;
dwTemp = 0;
ResetEvent(ov.hEvent);
fResult = WaitCommEvent(hCom, &dwTemp, &ov); // Тут ставлю ожидание ответа(эхо) из модема
if (fResult == FALSE)
{
dwError = GetLastError();
/* Ошибки кроме ERROR_IO_PENDING у меня здесь не возникает*/
if (dwError != ERROR_IO_PENDING) ErrorMessage("WaitCommError():", dwError);
}
dwTemp = WaitForMultipleObjects(2, array, FALSE, INFINITE); // А здесь жду события
if (dwTemp == WAIT_OBJECT_0)
{
CloseHandle(hCom);
/* Выход*/
}

/* Читаю данные из порта */
ZeroMemory(Buffer, 0x104);
dwTemp = 0;
ResetEvent(ov.hEvent);
fResult = ReadFile(hCom, Buffer, 0x100, &dwTemp, &ov);
if (fResult == FALSE)
{
array[0] = hExit;
array[1] = ov.hEvent;
dwTemp = WaitForMultipleObjects(2, array, FALSE, INFINITE);
if (dwTemp == WAIT_OBJECT_0)
{
CloseHandle(hCom);
/* Выход*/
}
dwTemp = 0;
fResult = GetOverlappedResult(hCom, &ov, &dwTemp, TRUE);
if (fResult == FALSE)
{
/* Сообщение и завершение */
}
}

// А здесь я вижу эхо, но не вижу OK от модема


Делается это на NT4, порт называется "COM1" или "COM2". Некоторые дополнительные проверки ошибок из исходника
убраны. С помощью Portmon'а я поглядел как работают некоторые другие программы. От того что у меня они отличаются в основном настройками, связанными с корекцией ошибок. Некоторые пишут в порт по одному байту(терминалы), но без TransmitCommChar(). Мне бы все же хотелось писать сразу всю строку инициализации, тем более я делаю не терминал.

Заранее спасибо. И еще хотелось бы что-нибудь найти по TAPI, референс в MSDN мне мало чем помог. Хочется что-нибудь типа "How to write TAPI 2.x applications.", чтобы была подробно описана сама методика работы с TAPI 2.
Re: COM port with WIN32 API
От: Eugene  
Дата: 28.06.01 18:20
Оценка:
Вызывает сомнение строка
cto.WriteTotalTimeoutMultiplier = 2*CBR_9600/dwBaudRate;

CBR_9600 определена как 9600, поэтому если скорость dwBaudRate больше,чем 19200, то WriteTotalTimeoutMultiplier = 0, а константа WriteTotalTimeoutConstant вообще не определена.

Хотя факт, что модем возвращает эхо, вроде говорит о том, что он данные получил. А в терминале-то модем откликается на ATZ\r? С параметрами DCB,контролем четности и т.д. все в порядке?

Для работы с COM-портами есть неплохой класс у PJ Naughter:

http://www.naughter.com/serialport.html

Из своего опыта: я использую WaitCommEvent для отслеживания изменения состояния CONNECT в отдельном потоке, запись/чтение делаю также в отдельном потоке, порт открываю в "Nonoverlapped" режиме,
регулируя временные характеристики протокола установкой констант COMMTIMEOUTS. Такой способ существенно (до банального) упрощает текст программы, реализующей собственно протокол, без какой-либо потери функциональности, ведь по сути приведенный однопоточный код все равно блокирует поток,
ожидая завершения каждой операции, но при этом код становится существенно сложнее. К примеру, задав нужные значения констант, достаточно сделать ReadFile и получить управление либо при успешном чтении, либо по истечении времени, определенного параметрами констант. Что касается записи, там это вообще некритично за счет многоуровневого буферирования (в том числе и в самом модеме). OVERLAPPED же режим был бы оправдан, если передающий или принимающий поток могли бы делать нечто полезное после начала операции и вплоть до ее завершения.

Евгений.
Don't trouble trouble until trouble troubles you
Re[2]: COM port with WIN32 API
От: Roman_M rgmroman.narod.ru
Дата: 29.06.01 05:23
Оценка:
Здравствуйте Eugene, вы писали:


E>Вызывает сомнение строка

E>cto.WriteTotalTimeoutMultiplier = 2*CBR_9600/dwBaudRate;
E>CBR_9600 определена как 9600, поэтому если скорость dwBaudRate больше,чем 19200, то WriteTotalTimeoutMultiplier = 0, а константа WriteTotalTimeoutConstant вообще не определена.

WriteTotalTimeoutConstant у меня равна нулю, просто в приведенном исходники отсутствовали строчки

COMMTIMEOUTS cto;
ZeroMemory(&cto, sizeof(COMMTIMEOUTS));

E>Хотя факт, что модем возвращает эхо, вроде говорит о том, что он данные получил. А в терминале-то модем откликается на ATZ\r?

В терминале модем нормально работает.

E>С параметрами DCB,контролем четности и т.д. все в порядке?

Параметры DCB ставятся без ошибок. Parity:NONE.


E>Для работы с COM-портами есть неплохой класс у PJ Naughter:


E>http://www.naughter.com/serialport.html


E>Из своего опыта: я использую WaitCommEvent для отслеживания изменения состояния CONNECT в отдельном потоке, запись/чтение делаю также в отдельном потоке, порт открываю в "Nonoverlapped" режиме,

E>регулируя временные характеристики протокола установкой констант COMMTIMEOUTS. Такой способ существенно (до банального) упрощает текст программы, реализующей собственно протокол, без какой-либо потери функциональности, ведь по сути приведенный однопоточный код все равно блокирует поток,
E>ожидая завершения каждой операции, но при этом код становится существенно сложнее. К примеру, задав нужные значения констант, достаточно сделать ReadFile и получить управление либо при успешном чтении, либо по истечении времени, определенного параметрами констант. Что касается записи, там это вообще некритично за счет многоуровневого буферирования (в том числе и в самом модеме). OVERLAPPED же режим был бы оправдан, если передающий или принимающий поток могли бы делать нечто полезное после начала операции и вплоть до ее завершения.

Я тоже использую два потока. Один управляет окном, другой работает с момдемом. OVERLAPPED использую для того, чтобы одновременно можно было ждать как минимум два события: завершения вывода и завершения программы. В синхронном режиме скорее всего придется делать TerminateThread при выходе из программы. Кроме того, я слышал, что Win2K не дает открыть порт синхронном режиме.
Re[3]: COM port with WIN32 API
От: Eugene  
Дата: 29.06.01 14:40
Оценка:
Здравствуйте Roman_M, вы писали:
...
RM>WriteTotalTimeoutConstant у меня равна нулю, просто в приведенном исходники отсутствовали строчки

Вот это и смущает. MSDN "утверждает", что если WriteTotalTimeoutConstant = WriteTotalTimeoutMultiplier = 0, эти константы не используются, что не вполне понятно.
IMHO, следует дать им что-нибудь "определенное", например:
cto.WriteTotalTimeoutMultiplier = 2*CBR_9600/dwBaudRate ? 2*CBR_9600/dwBaudRate : 1; // не менее 1
cto.WriteTotalTimeoutConstant = 50;

Еще одно соображение по поводу оригинального кода. Насколько я понимаю, WaitCommEvent в асинхронном режиме не нужен — его можно "опустить". Точнее он бывает нужен в специфических случаях, к примеру, для побайтового приема с максимально быстрым возвратом эхо. В приведенном же примере при получении уведомления о приеме 1-го байта все равно вызывается ReadFile для приема остальных. Мешать, вроде, не должен, но смысла не несет.

RM>В синхронном режиме скорее всего придется делать TerminateThread при выходе из программы.

Совсем не обязательно: соответствующая установка констант COMMTIMEOUTS обеспечивает активизацию потока через заданное время, а в цикле потока можно анализировать условие выхода и делать корректный выход. Если же поток использует WaitCommEvent, то вызов в другом потоке SetCommMask(0) выводит его из состояния ожидания, что, впрочем, надо делать и в асинхронном режиме.

RM>Кроме того, я слышал, что Win2K не дает открыть порт синхронном режиме.

Дает, это легко проверить.

Евгений.
Don't trouble trouble until trouble troubles you
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.