реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> WinAPI - Serial communications, COM-порт по трем проводам
vazz
сообщение Jan 6 2015, 13:31
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 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. Что я упустил?


--------------------
Не так страшна автоматизация, как её малюют.
Go to the top of the page
 
+Quote Post
Xenia
сообщение Jan 6 2015, 14:22
Сообщение #2


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Попробуйте заменить
FILE_FLAG_OVERLAPPED
на
FILE_FLAG_NO_BUFFERING

А еще конкретизируйте номер порта.
Т.е.
"\\\\.\\COM"
замените на COM с номером, типа:
"\\\\.\\COM1"
Go to the top of the page
 
+Quote Post
Палыч
сообщение Jan 6 2015, 14:31
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Поскольку Вы при открытии файла указали флаг FILE_FLAG_OVERLAPPED, то при вызове процедуры WriteFile должны обязательно передавать указатель на OVERLAPPED структуру (этот указатель не может принимать значение NULL).
Go to the top of the page
 
+Quote Post
vazz
сообщение Jan 6 2015, 14:42
Сообщение #4


Частый гость
**

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



Xenia, я пробовал играться с этим флагом. Я так понял вариантов для com-портов (не файлов) всего два: 0 (для синхронного обмена, он проще) и FILE_FLAG_OVERLAPPED (для асинхронного). Ни тот ни другой, ни предложенный Вами не меняют итоговый результат - ACCESS DENIED.

Дополнительно отмечу несколько моментов:

- мое приложение после компиляции запускается от имени администратора (ОС Windows 7 x64);

- память под буфер передачи (данных) я формирую аналогично выделению памяти под упомянутые структуры (т.е. данные участки памяти реально доступны для модификации, я перепроверял несколько раз различными способами);

- com-порт не используется другими процессами (т.е. работе с ним ничто не мешает);

- пробовал работать с портом программами типа putty - все получается (и открыть и отправить и принять);

Есть несколько подозрительных вопросов:

1. Я самостоятельно могу запросить кусок памяти своими средствами для буфера приема/передачи или я должен пользоваться какими-то дополнительными WinAPI-функциями для задания такой области памяти под буферизацию? В моем коде я "сам" выделяю память и передаю ее поинтер в функцию WriteFile.

2. Необходимо ли для простой отправки данных типа "Hello world" создание каких либо дополнительных структур типа OVERLAPED или структур отвечающих за ошибки обмена и события при обмене? Может из-за отсутствия этих или каких-то либо других дополнительных структур функция WriteFile возвращает ошибку доступа?


--------------------
Не так страшна автоматизация, как её малюют.
Go to the top of the page
 
+Quote Post
Xenia
сообщение Jan 6 2015, 14:43
Сообщение #5


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Ну, а номер COM-порту вы назначили?
Go to the top of the page
 
+Quote Post
vazz
сообщение Jan 6 2015, 14:53
Сообщение #6


Частый гость
**

Группа: Участник
Сообщений: 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.


--------------------
Не так страшна автоматизация, как её малюют.
Go to the top of the page
 
+Quote Post
vazz
сообщение Jan 14 2015, 10:25
Сообщение #7


Частый гость
**

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



Неужели ни у кого нет опыта написания библиотеки/модуля для работы с com-портом с использованием WinAPI? Если есть опыт, подскажите перечень обязательных структур/переменных, которые необходимо создать/заполнить/передать/вернуть, чтобы можно было отправить данные и принять ответ. Хотя бы в синхронном "топорном" режиме для начала, можно без обработки событий и прочих доп.функций (с ними я по ходу дела ознакомлюсь). В инете ничего не могу найти, кроме обрывков, которые не дают отчетливого представления хотя бы для того, чтобы отправить в порт "Hello world". Хелп!


--------------------
Не так страшна автоматизация, как её малюют.
Go to the top of the page
 
+Quote Post
SM
сообщение Jan 14 2015, 12:27
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 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 - это не я, это форум глючит.
Go to the top of the page
 
+Quote Post
RabidRabbit
сообщение Jan 14 2015, 12:39
Сообщение #9


Местный
***

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



До кучи sm.gif

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;
}

Go to the top of the page
 
+Quote Post
juvf
сообщение Jan 19 2015, 08:28
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 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;
}

Go to the top of the page
 
+Quote Post
vazz
сообщение Dec 25 2016, 06:55
Сообщение #11


Частый гость
**

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



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


--------------------
Не так страшна автоматизация, как её малюют.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Dec 25 2016, 07:51
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(vazz @ Dec 25 2016, 09:55) *
что за фигня у меня, у кого какие мысли?

Есть такая мысль, что (GENERIC_READ + GENERIC_WRITE) в вашем случае оказывается не равно (GENERIC_READ | GENERIC_WRITE).
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 26 2016, 06:02
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 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


будут они равны, хоть ОРь, хоть складывай
Go to the top of the page
 
+Quote Post
gerber
сообщение Dec 27 2016, 15:22
Сообщение #14


Знающий
****

Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088



Цитата(toweroff @ Dec 26 2016, 09:02) *
будут они равны, хоть ОРь, хоть складывай

При сложении будет учтен знак, если константы signed.


--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 27 2016, 21:03
Сообщение #15


Гуру
******

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



Цитата(gerber @ Dec 27 2016, 18:22) *
При сложении будет учтен знак, если константы signed.

Ну константы как-то не предполагают знак
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 17:22
Рейтинг@Mail.ru


Страница сгенерированна за 0.01518 секунд с 7
ELECTRONIX ©2004-2016