Работа с COM-портом в C++

229
23 февраля 2018, 18:38

В сети есть куча мануалов и примеров по работе с портом в C++, но у меня как-то неохотно они работают. Изначально юзал QSerialPort, все было прекрасно до тех пор, пока случайно не выяснил, что QT-шный waitForReadyRead() забагован и кладет мою программу когда ему вздумается.

Решил попробовать поработать с HANDLE, но лыжи пока не едут.

#include <iostream>
#include <windows.h>
using namespace std;
int main(int argc, char *argv[])
{
    //LPCTSTR sPortName = L"COM:6";
    TCHAR *pcCommPort = L"COM:6";
    //serialPort = CreateFile(pcCommPort, GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
    HANDLE serialPort = CreateFile( pcCommPort,
                       GENERIC_READ | GENERIC_WRITE,
                       0,      //  must be opened with exclusive-access
                       NULL,   //  default security attributes
                       OPEN_EXISTING, //  must use OPEN_EXISTING
                       0,      //  not overlapped I/O
                       NULL ); //  hTemplate must be NULL for comm devices
    if(serialPort==INVALID_HANDLE_VALUE)
    {
        cout << "open!\n";
    }
    else
    {
        DWORD lastError = GetLastError();
        cout << "cant open! \n";
        cout << lastError << "\n";
    }
    DCB dcbSerialParams;
    FillMemory(&dcbSerialParams, sizeof(dcbSerialParams), 0); //zero initialize the structure
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);      //fill in length
    if (!GetCommState(serialPort, &dcbSerialParams))
    {
        cout << "getting state error\n";
    }
    dcbSerialParams.BaudRate=CBR_115200;
    dcbSerialParams.ByteSize=8;
    dcbSerialParams.StopBits=ONESTOPBIT;
    dcbSerialParams.Parity=NOPARITY;
    if(!SetCommState(serialPort, &dcbSerialParams))
    {
        cout << "error setting serial port state\n";
    }
    DWORD nb;
    OVERLAPPED ov;
    uint8_t buffer[] = {0xAB, 0x01, 0x02, 0x02, 0x05, 0xFF, 0xF7};
    WriteFile(serialPort,buffer,sizeof(buffer),&nb,&ov);
    CloseHandle(serialPort);
    return 0;
}

Если я напишу "COM6", что вполне логично, то порт открыть не получается. Зато спокойно открывает "COM:6" и COM-ы 7, 11, 15 и т.д., которых и в помине нет. Выгрузить и установить настойки скорости, например, тоже не получается -- вываливается ошибка.

Что я делаю не так?

Edit: Прошу прощения, после 2-3 часового тупняка решил спросить здесь от безнадеги. Я понял, что открываются все порты, кроме тех, которые реально существуют, проверил if(..), оказалось выдернул где-то неверный код. Должно быть так:

if(serialPort!=INVALID_HANDLE_VALUE){
    cout << "open!\n";
}
else{
    cout << "cant open! \n";
}
Answer 1

Для открытия порта лучше использовать вот такое имя port = "\\\\.\\COM2";
1) с хэндлом сами нашли if(serialPort!=INVALID_HANDLE_VALUE)
1.1) Для работы с портом с помощью асинхронного ио необходим флаг FILE_FLAG_OVERLAPPED
2) Для установки очереди SetupComm(...)
3) Для установки таймаутов SetCommTimeouts(...)
4) Для установки скоростей/четности и т.п. SetCommState(...)
5) Для установки маски событий на порту SetCommMask(...)
6) Для выполнения доп. функций EscapeCommFunction(...)
7) Для очистки линии PurgeComm(...)
8) Для сброса ошибок ClearCommError(...)

Для асинхронного ио нужно создавать структуру примерно так:

OVERLAPPED m_ovRead;
ZeroMemory(&m_ovRead, sizeof(m_ovRead));
m_ovRead.hEvent = CreateEvent(NULL, false, false, NULL);

Ивент этой структуры это собственно то, что будет сигнализировать о завершении операций чтения/записи, дальше эту структуру можно использовать в функциях GetOverlappedResultEx(...)

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

При получении сигнала EV_RXCHAR можно читать из порта.

В общем это основной набор функций для работы с портами, все функции детально описаны на мсдне, даже с приерами. Код получается громоздким, но достаточно написать его 1 раз и любой ком-девайс к вашим услугам.

Answer 2

Прошу прощения, после 2-3 часового тупняка решил спросить здесь от безнадеги. Я понял, что открываются все порты, кроме тех, которые реально существуют, проверил if(..), оказалось выдернул где-то неверный код. Должно быть так:

if(serialPort!=INVALID_HANDLE_VALUE){
    cout << "open!\n";
}
else{
    cout << "cant open! \n";
}
READ ALSO
Вывод всех значений вектора

Вывод всех значений вектора

Необходимо в цикле вывести все значения вектора

169
Как реализуется iтая переменная в C++

Как реализуется iтая переменная в C++

Допустим у меня серия уравнений, и выходит так, x(2) это x(1) - n (и далее это тоже продолжается)Можно ли как-то задать один раз переменную x, чтобы...

216
Ошибка при выводе данных из Struct

Ошибка при выводе данных из Struct

При сьемке фотографий от камеры, записываю имя файла (получается путь) и id в вектор

206
Qwt Analog Widget

Qwt Analog Widget

Я скачал qwtМне нужны там только виджеты

239