Последовательные порты полюбились разработчикам за их простоту в обслуживании и использовании.
И конечно же писать в консоль терминальной программы это всё хорошо, но хочеться своё приложение, которое по нажатии клавиши на экране выполняет нужные вам действия 😉
В этой статье опишу как работать с com портом на языке Си++.
Решение простое, но почемуто рабочий пример найден был не сразу. За сим сохраняю его тут.
Конечно вы можете использовать кроссплатформенные решения вроде QSerial — библиотеки в составе Qt, я наверное так и сделаю, но в будующем. Сейчас же речь о «чистом» виндовском C++. Писать будем в Visual Studio. У меня 2010, хотя роли это никакой не играет…
Создаём новый консольный Win32 проект.
Инклудим header файлы:
#include <windows.h> #include <iostream> using namespace std;
Объявляем обработчик com порта:
HANDLE hSerial;
Я делаю это глобально, чтобы не заморачиваться с указателями при передаче его в функции.
Дальше начинаем формировать функцию main:
int _tmain(int argc, _TCHAR* argv[]) {
Терпеть не могу виндовский стиль программирования. Обозвали всё посвоему и сидят радуются…
Теперь магия объявления строки с именем порта. Дело в том, что char оно преобразовывать само не умеет.
LPCTSTR sPortName = L"COM1";
Работа с последоавательными портами в Windows проходит как с файлом. Открываем первый ком порт для записи/чтения:
hSerial = ::CreateFile(sPortName,GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
Проверяем работоспособность:
if(hSerial==INVALID_HANDLE_VALUE) { if(GetLastError()==ERROR_FILE_NOT_FOUND) { cout << "serial port does not exist.\n"; } cout << "some other error occurred.\n"; }
Теперь нужно настроить параметры соединения:
DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength=sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { cout << "getting state error\n"; } dcbSerialParams.BaudRate=CBR_9600; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY; if(!SetCommState(hSerial, &dcbSerialParams)) { cout << "error setting serial port state\n"; }
На msdn советуют сначала получить параметры, а затем менять их. Мы ещё только учимся, поэтому делаем как просят.
Теперь объявим строку, которую будем передавать и переменные необходимые для этого:
char data[] = "Hello from C++"; // строка для передачи DWORD dwSize = sizeof(data); // размер этой строки DWORD dwBytesWritten; // тут будет количество собственно переданных байт
Посылаем строку. Напомню, что пример простейший, поэтому никаких особо проверок я не делаю:
BOOL iRet = WriteFile (hSerial,data,dwSize,&dwBytesWritten,NULL);
Также я решил вывести для контроля размер строки и количество отосланных байт:
cout << dwSize << " Bytes in string. " << dwBytesWritten << " Bytes sended. " << endl;
В конце программы делаем бесконечный цикл чтения данных:
while(1) { ReadCOM(); } return 0; }
Теперь функция чтения:
void ReadCOM() { DWORD iSize; char sReceivedChar; while (true) { ReadFile(hSerial, &sReceivedChar, 1, &iSize, 0); // получаем 1 байт if (iSize > 0) // если что-то принято, выводим cout << sReceivedChar; } }
Вот собственно и весь пример.
Я создал виртуальный com порт. И слал из COM1 в COM2:
Из нашей программы было отправлено «Hello from C++», а из терминала «hello how2.org.ua».
Скачать пример.
Добрый день. Прочитал в этой статье что нужно перед закрытием программы закрыть порт: http://ru.wikibooks.org/wiki/COM-%D0%BF%D0%BE%D1%80%D1%82_%D0%B2_Window… . Зачем это нужно и чем это чревато?
Таким образом вы гарантированно оканчиваете работу с портом. Иначе может возникнуть ситуация, когда при повторном обращении после вашей программы доступ к порту получить нельзя (система считает, что он используется).
Хотя у меня таких проблем не возникало. Видимо со времен dos систему улучшили в этом плане.
Как реализовать прием и передачу чисел? Я смог это реализовать только с 32 битным int. Но UART передает по 8 байт, и как правило данные 8-битные.
Добрый день. Подскажите пожалуйста, как правильно передавать 8-ми битные числа через com порт. Я пробовал передавать обычный int, но задавать его размер равным 1, в итоге приходит мусор. Пробовал также uint8_t, но в C++ он реализован очень криво и вместо чисел передаются символы char.
P.S. Написал второй комментарий потому что не уверен отправился ли первый.
Заранее спасибо за ответ.
Извиняюсь,я чайник.Что такое строка DCB dcbSerialParams = {0};?
так создается структура для хранения настроек
Здравствуйте мне нужно вместо строки отправить команду "G826" как это сделать?
В с++ я новичёк
Я не специалист в g826 но думаю Вам должно быть все равно, заправляете буфер с коммандой и шлете.
спасибо, попробую
Здравствуйте, у меня есть некоторые проблемы: COM порт никак не хочет работать. С самого начала, я всё смахивал на то, что я чайник. Но не тут — то было! Я много раз пытался писать код. В итоге, я просто скачал пример.(У меня раньше не было возможностей) И что же? Запускаю программу из примера (не изменяя код) и всё равно не работает! Выводятся только ошибки. Я, извините за выражение, тупой? Или у меня что — то не так? Заранее огромное спасибо!
Спасибо ! Пожалуй единственный код который завелся с пол пинка.
15 Bytes sended. :o)
А можно ли обойтись без бесконечного цикла, а обрабатывать событие возникновения данных на СОМ порте? Как я полагаю этот цикл затрачивает много ресурсов и что то более серьёзное сделать с этим примером не получится.
нужно что-то вроде системного вызова select в linux. честно говоря не в курсе как это в винде сделать
Заколебался — при отправке висло все напрочь. и дело не в циклах оказалось.
Просто если не настроить таймауты, то ответа будет ждать очень долго (при этом графический интерфейс , если у вас он есть, повисает напрочь).
Допустим если как у меня — readFile по таймеру происходит, то надо в конфигурацию порта добавить следующий код:
//настраиваем таймауты на отсутствие ожидания ответа(иначе ReadFile повесит программу)
COMMTIMEOUTS CommTimeOuts={0xFFFFFFFF,0,0,0,1500};
if(!SetCommTimeouts(hSerial, &CommTimeOuts))
{
CloseHandle(hSerial);
hSerial = INVALID_HANDLE_VALUE;
ShowMessage("Ошибка при записи таймаутов порта");
}
Тогда по readFile будут сразу отдаваться байты какие есть без ожидания ответа.
Ищу пример программирования на форме обмен через компонент SerialPort, желательно в асинхронном режиме.
Спасибо! наконец то нашел)) жалко правда что не ++, но всё же!))
Скачал ваш код, запустил в Visual studio 2015 вышла ошибка при компляции "
Ошибка LNK1207 несовместимый формат PDB в "C:\Users\Николай\Desktop\com_port_test\Debug\com_port_test.pdb"; удалите и перестройте com_port_test C:\Users\Николай\Desktop\com_port_test\com_port_test\LINK 1
" что делать?,заранее благодарю за ответ
Как настроить совместную работу данноой программы с терминалом порта (как в примере) пишет, что невозможно использовать терминал одновременно с другим ПО
в примере два программных порта соединенных между собой
https://blablacode.ru/windows/413
А как конкретно отсылать и получать информацию с порта на порт. Код скопипастил, а куда название порта-приемника и порта-получателя вписать? В функции для чтения не могу найти имя порта-получателя, а функцию отправки вовсе не найду. Пожалуйста, помогите
Спасибо, очень помогло
А зачем нам argc и argv?
Поклон в ноги автору сей проги! Заработало сразу (разумеется я изменил № ком порта). Скажите пожалуйста, как узнать кол-во доступных байтов для считывания с порта? Также можете ответить мне (ProHacker2006) на киберфоруме — http://www.cyberforum.ru/cpp/thread2147367.html
я бы лучше использовал Qt с QSerial, на win api нужно все писать, а в qt готово