Хотелось бы получить какие-нибудь полезные советы, подсказки и комментарии от тех кто уже освоил программирование COM портов на Win32 API. На данный момент я застрял на посылке данных в модем. Вот в кратце приведу код, который я написал:
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.
Вызывает сомнение строка
cto.WriteTotalTimeoutMultiplier = 2*CBR_9600/dwBaudRate;
CBR_9600 определена как 9600, поэтому если скорость dwBaudRate больше,чем 19200, то WriteTotalTimeoutMultiplier = 0, а константа WriteTotalTimeoutConstant вообще не определена.
Хотя факт, что модем возвращает эхо, вроде говорит о том, что он данные получил. А в терминале-то модем откликается на ATZ\r? С параметрами DCB,контролем четности и т.д. все в порядке?
Для работы с COM-портами есть неплохой класс у PJ Naughter:
Из своего опыта: я использую WaitCommEvent для отслеживания изменения состояния CONNECT в отдельном потоке, запись/чтение делаю также в отдельном потоке, порт открываю в "Nonoverlapped" режиме,
регулируя временные характеристики протокола установкой констант COMMTIMEOUTS. Такой способ существенно (до банального) упрощает текст программы, реализующей собственно протокол, без какой-либо потери функциональности, ведь по сути приведенный однопоточный код все равно блокирует поток,
ожидая завершения каждой операции, но при этом код становится существенно сложнее. К примеру, задав нужные значения констант, достаточно сделать ReadFile и получить управление либо при успешном чтении, либо по истечении времени, определенного параметрами констант. Что касается записи, там это вообще некритично за счет многоуровневого буферирования (в том числе и в самом модеме). OVERLAPPED же режим был бы оправдан, если передающий или принимающий поток могли бы делать нечто полезное после начала операции и вплоть до ее завершения.
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 не дает открыть порт синхронном режиме.
Здравствуйте 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 не дает открыть порт синхронном режиме.
Дает, это легко проверить.