|
WinAPI - Serial communications, COM-порт по трем проводам |
|
|
|
Jan 6 2015, 13:31
|

Частый гость
 
Группа: Участник
Сообщений: 189
Регистрация: 21-01-10
Пользователь №: 54 971

|
Раньше никогда не сталкивался с необходимостью осваивать WinAPI в части работы с COM-портами, другие способы коммуникации как-то больше волновали. А теперь вот не могу справится с простейшей на мой первоначальный взгляд задачей - реализация асинхронного обмена через "банальный" COM-порт. К COM-порту подключен модем, "понимающий" стандартный набор AT-команд. Вот и решил я далеко не ходить и не разгребать глюки готовых классов и модулей, реализованных другими разработчиками. Лицензионные продукты для своей простой задачи не вижу смысла покупать. Вот пал выбор на WinAPI. Вроде бы и информации в сети валом и msdn внимательно почитал, но у меня то ли неправильно выстроился алгоритм программы то ли дядюшка билли со своими извращенными алгоритмами всевозможных ограничений прав доступа к памяти мешает. Прошу помощи. Успешно открываю порт Код -- kernel32 - подгружаемая библиотека WinAPI-функций -- comid - номер открываемого порта -- handle - итоговый указатель на порт handle = kernel32.CreateFileA("\\\\.\\COM" .. comid, GENERIC_READ+GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0) Далее я создаю и заполняю структуры BCD и COMMTIMEOUTS. Сохраняю настройки с помощью функций SetCommState, SetCommTimeouts - обе возвращают TRUE. Код DCB.BaudRate = CBR_9600; DCB.ByteSize = 8; DCB.Parity = NOPARITY; DCB.StopBits = ONESTOPBIT; DCB.fBinary = 1; DCB.fOutxCtsFlow = 0; DCB.fOutxDsrFlow = 0; DCB.fDtrControl = DTR_CONTROL_DISABLE; DCB.fDsrSensitivity = 0; DCB.fNull = 0; DCB.fRtsControl = RTS_CONTROL_DISABLE; DCB.fAbortOnError = 0;
COMMTIMEOUTS.ReadIntervalTimeout = 10; COMMTIMEOUTS.ReadTotalTimeoutMultiplier = 1; COMMTIMEOUTS.ReadTotalTimeoutConstant = 100; COMMTIMEOUTS.WriteTotalTimeoutMultiplier = 0; COMMTIMEOUTS.WriteTotalTimeoutConstant = 0; Далее пытаюсь отправить данные в порт Код -- data - массив с данными -- b - указатель на переменную типа DWORD "счетчик байт" -- t - указатель на буфер с данными -- n - кол-во данных в массиве data local n = Table.Count(data); local b = MemoryEx.Allocate(4); local t = MemoryEx.Allocate(n); if (b and t) then -- сброс счетчика байт MemoryEx.Fill(b, 4, 0, MEMEX_BYTE); -- заполнение буфера данными из массива data local c = 1; repeat MemoryEx.Byte(t + c - 1, data[c]); c = c + 1; until c > n; -- запись в порт if (kernel32.WriteFile(handle, t, n, b, 0) == 0) then local e = kernel32.GetLastError(); Dialog.Message("error", e); end end После попытки записать что-либо в порт функция возвращает 5, т.е. ACCESS DENIED. Что я упустил?
--------------------
Не так страшна автоматизация, как её малюют.
|
|
|
|
|
Jan 6 2015, 14:53
|

Частый гость
 
Группа: Участник
Сообщений: 189
Регистрация: 21-01-10
Пользователь №: 54 971

|
Цитата(Палыч @ Jan 6 2015, 18:31)  Поскольку Вы при открытии файла указали флаг FILE_FLAG_OVERLAPPED, то при вызове процедуры WriteFile должны обязательно передавать указатель на OVERLAPPED структуру (этот указатель не может принимать значение NULL). Спасибо за ответ. Я пробовал сбрасывать этот флаг до значения 0. При этом ошибка ACCESS DENIED все равно возвращается функцией WriteFile. Цитата(Xenia @ Jan 6 2015, 18:43)  Ну, а номер COM-порту вы назначили? Обижаете, конечно. Порт успешно открывается. Во-первых возвращается корректный handle (не 0 и не -1). Дополнительно после открытия порта делал проверку - запускал putty и убеждался в занятости порта. Номер порта в моем случае 52, я передаю этот номер в функцию CreateFileA. Цитата(Xenia @ Jan 6 2015, 18:22)  замените на COM с номером, типа: "\\\\.\\COM1" Для универсальности я беру строку "\\\\.\\COM" и "прилепляю" к концу этой строки номер порта. Т.е. задаю параметр comid = 52 и передаю в функцию. В моем посте #1 в первом куске кода это видно. Я пока играюсь в visual-среде, "движок" которой написан на Lua.
--------------------
Не так страшна автоматизация, как её малюют.
|
|
|
|
|
Jan 14 2015, 12:27
|
Гуру
     
Группа: Свой
Сообщений: 7 946
Регистрация: 25-02-05
Из: Moscow, Russia
Пользователь №: 2 881

|
Да есть опыт... Но ничего такого не видно, почему бы оно не должно у Вас работать.... Код hc=CreateFile(port,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NU LL);
.... BuildCommDCB(DEFDCB_STRING,&dcb); memset(&cc,0,sizeof(cc)); cc.dwSize=sizeof(cc); cc.wVersion=1; cc.dwProviderSubType=PST_RS232; memcpy(&cc.dcb,dcb,sizeof(DCB)); rc=SetCommConfig(hc,&cc,sizeof(cc));
....
memset(&ct,0,sizeof(ct)); ct.ReadIntervalTimeout=200; ct.ReadTotalTimeoutConstant=250; SetCommTimeouts(hc,&ct);
....
EscapeCommFunction(hc,SETDTR); EscapeCommFunction(hc,SETRTS);
....
WriteFile(hc,ovr->data.buf,bytes,&dummy,(LPOVERLAPPED) ovr); И все арбайтен. Ну то, что ссылка на буфер данных хранится в структуре OVERLAPPED, это уже нюансы реализации. Это совершенно не обязательно. И что NULL перенесло на NU и LL - это не я, это форум глючит.
|
|
|
|
|
Jan 14 2015, 12:39
|

Местный
  
Группа: Свой
Сообщений: 397
Регистрация: 3-12-09
Из: Россия, Москва
Пользователь №: 54 040

|
До кучи  CODE bool CComm6055::exchangeData( QByteArray & cmd ) { COMMTIMEOUTS ct; DCB dcb; HANDLE h; BOOL rc; bool result = false; DWORD bytesWritten, bytesRead;
// h = CreateFile( (LPCWSTR)sPort.unicode() , GENERIC_READ | GENERIC_WRITE , 0 , 0 , OPEN_EXISTING , 0 , 0 ); // if ( INVALID_HANDLE_VALUE != h ) { // ZeroMemory( &dcb, sizeof(dcb) ); dcb.BaudRate = CBR_9600; dcb.fBinary = TRUE; dcb.ByteSize = 8; rc = SetCommState( h, &dcb ); // ct.ReadIntervalTimeout = 64; ct.ReadTotalTimeoutConstant = 0; ct.ReadTotalTimeoutMultiplier = 0; ct.WriteTotalTimeoutConstant = 0; ct.WriteTotalTimeoutMultiplier = 0; rc = SetCommTimeouts( h, &ct ); // makeCheckSum( cmd ); // if ( WriteFile( h , cmd.data() , cmd.size() , &bytesWritten , 0 ) ) { // if ( cmd.size() == bytesWritten ) { // cmd.resize( 32 ); // if ( ReadFile( h , cmd.data() , 32 , &bytesRead , 0 ) ) { // cmd.resize( bytesRead ); // result = true; } } } // CloseHandle( h ); } // return result; }
|
|
|
|
|
Jan 19 2015, 08:28
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(vazz @ Jan 14 2015, 15:25)  Неужели ни у кого нет опыта написания библиотеки/модуля для работы с com-портом с использованием WinAPI? Опыт есть. Один раз настроил и забыл. Копипаст из проекта в проект. Работает как часы. Код bool MainWindow::openComPort(bool is19200) { std::string nameComPort = comPort->currentText().toStdString(); std::wstring wComPort; wComPort.resize(nameComPort.size()); for(unsigned int i=0; i<nameComPort.size(); i++) wComPort[i] = (WCHAR)nameComPort.at(i); wComPort.insert(0, L"\\\\.\\"); port=CreateFile( wComPort.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if(port == INVALID_HANDLE_VALUE) { QApplication::restoreOverrideCursor(); QMessageBox::critical(this, "Spyhole error", QString("Can not open com port %1").arg(QString::fromStdString(nameComPort))); return false; } else { GetCommState(port,&dcb); //memset(&dcb, 0, sizeof(DCB)); dcb.DCBlength=sizeof(DCB); std::wstring config;
std::string tConfig = is19200 ? "baud=19200 parity=N data=8 stop=1" : "baud=115200 parity=N data=8 stop=1"; config.resize(tConfig.size()); for(unsigned int i=0; i<tConfig.size(); i++) config[i] = (WCHAR)tConfig.at(i);
BuildCommDCB(config.c_str(),&dcb); dcb.fNull=FALSE; ct.ReadIntervalTimeout=1000; ct.ReadTotalTimeoutMultiplier=0; ct.ReadTotalTimeoutConstant=5000; if( !SetCommState(port,&dcb)) { CloseHandle(port); port = NULL; QApplication::restoreOverrideCursor(); QMessageBox::critical(this, "Spyhole error", QString("Cann't set config of %1").arg(QString::fromStdString(nameComPort))); return false; } SetCommTimeouts(port,&ct); PurgeComm(port,PURGE_TXCLEAR|PURGE_RXCLEAR); SetupComm(port,4500,4500); } return true; } Код bool MainWindow::readCurve(CURVE curve, unsigned char *replay) { unsigned char command[20]; ...
WriteFile(port,command, 8, &BytesCnt, NULL);
ReadFile(port, replay, 4000, &BytesCnt, NULL); if(BytesCnt != 4000 ) { QApplication::restoreOverrideCursor(); QMessageBox::critical(this, "Spyhole error", QString("Can not receive 4000 byte. Received %1 bytes").arg(BytesCnt)); return false; } return true; }
|
|
|
|
|
Dec 25 2016, 06:55
|

Частый гость
 
Группа: Участник
Сообщений: 189
Регистрация: 21-01-10
Пользователь №: 54 971

|
В общем приходится периодически возвращаться к этой теме  видимо, хочешь не хочешь, а освоить работу с com-портами мне придется. На текущий момент повспоминал все что было, решил создать простенькое windowed приложение для теста com-портов. Так вот, возвращаясь к ранее обсуждаемой на этом форуме проблеме, сообщаю, что как и ранее открыть порт удается, настроить тоже (проверяю записаны ли настройки непосредственно по поинтеру зарезервированного под них куска памяти). Записать данные удалось (вроде бы тут должно быть "уррра!!!"), но только в одном случае - при открытии порта функцией CreateFile я передаю ей параметр dwDesiredAccess равный GENERIC_WRITE. Если при вызове этой функции я указываю GENERIC_READ + GENERIC_WRITE, то запись в порт не происходит. Порт открываю пока обычном режиме (NOT OVERLAPPED). Вопрос: если я открываю порт в НЕ асинхронном режиме, то я могу открыть его только "в одностороннем режиме", т.е. открываю порт для записи, потом закрываю порт и если мне нужно получить ответ от устройства, то я снова открываю порт но уже в режиме чтения и читаю данные? Бредовенько как-то.. В инете куча примеров где в 100% случаев права при вызове функции CreateFile указываются именно как "GENERIC_READ | GENERIC_WRITE", что за фигня у меня, у кого какие мысли?
--------------------
Не так страшна автоматизация, как её малюют.
|
|
|
|
|
Dec 26 2016, 06:02
|

Гуру
     
Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514

|
Цитата(aaarrr @ Dec 25 2016, 10:51)  Есть такая мысль, что (GENERIC_READ + GENERIC_WRITE) в вашем случае оказывается не равно (GENERIC_READ | GENERIC_WRITE). хмм... Код #define GENERIC_READ 0x80000000 #define GENERIC_WRITE 0x40000000 #define GENERIC_EXECUTE 0x20000000 #define GENERIC_ALL 0x10000000 будут они равны, хоть ОРь, хоть складывай
|
|
|
|
|
Dec 27 2016, 15:22
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(toweroff @ Dec 26 2016, 09:02)  будут они равны, хоть ОРь, хоть складывай При сложении будет учтен знак, если константы signed.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|