Visual studio управление com портом
В сети есть куча мануалов и примеров по работе с портом в C++, но у меня как-то неохотно они работают. Изначально юзал QSerialPort, все было прекрасно до тех пор, пока случайно не выяснил, что QT-шный waitForReadyRead() забагован и кладет мою программу когда ему вздумается.
Решил попробовать поработать с HANDLE , но лыжи пока не едут.
Если я напишу "COM6", что вполне логично, то порт открыть не получается. Зато спокойно открывает "COM:6" и COM-ы 7, 11, 15 и т.д., которых и в помине нет. Выгрузить и установить настойки скорости, например, тоже не получается -- вываливается ошибка.
Что я делаю не так?
Edit: Прошу прощения, после 2-3 часового тупняка решил спросить здесь от безнадеги. Я понял, что открываются все порты, кроме тех, которые реально существуют, проверил if(..) , оказалось выдернул где-то неверный код. Должно быть так:
Курите мануалы, там все работает, проверено. И скорости настраиваются, во всяком случае начиная с винды 2000. Только чтобы открыть порты начиная с номера 10 надо как-то хитро их обозвать. То-ли два слеша в начале поставить, то-ли еще чего-то. Сейчас не помню точно, но все есть в интернете. Советую написать класс - обертку над API портов, отладить один раз и потом будете пользоваться всю жизнь и горя не знать. Все равно все сторонние поделки это такие же надстройки над виндовским API портов. Хотя сейчас и машин с портами не осталось, только преобразователи COM-USB. Спасибо за совет. Про хитрости имени COM10+ в курсе, их тоже пробовал. Обновил пост: как обычно, по невнимательности натупил.Для открытия порта лучше использовать вот такое имя 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(. )
Для асинхронного ио нужно создавать структуру примерно так:
Ивент этой структуры это собственно то, что будет сигнализировать о завершении операций чтения/записи, дальше эту структуру можно использовать в функциях GetOverlappedResultEx(. )
Для чтения можно подписаться на ожидание сигналов на порту с помощью функции WaitCommEvent(. ) , который вернет вам управление при получении одного из сигналов, заданных в SetCommMask(. )
При получении сигнала EV_RXCHAR можно читать из порта.
В общем это основной набор функций для работы с портами, все функции детально описаны на мсдне, даже с приерами. Код получается громоздким, но достаточно написать его 1 раз и любой ком-девайс к вашим услугам.
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Представляет ресурс последовательного порта.
Примеры
В следующем примере кода показано использование SerialPort класса, чтобы два пользователя могли общаться с двух отдельных компьютеров, Соединенных нуль-модемным кабелем. В этом примере пользователям предлагается ввести параметры порта и имя пользователя перед разговором. Для обеспечения полной функциональности этого примера оба компьютера должны выполнять программу.
Комментарии
Используйте этот класс для управления файловым ресурсом последовательного порта. Этот класс обеспечивает синхронный и управляемый событиями ввод-вывод, доступ к состояниям ПИН-кода и прерываний, а также доступ к свойствам последовательного драйвера. Кроме того, функциональные возможности этого класса могут быть заключены во внутренний Stream объект, доступны через BaseStream свойство и переданы в классы, которые переносятся в оболочку или используют потоки.
SerialPortКласс поддерживает следующие кодировки: ASCIIEncoding , UTF8Encoding , UnicodeEncoding , UTF32Encoding и любую кодировку, определенную в mscorlib.dll, где кодовая страница меньше 50000 или кодовая страница 54936. Можно использовать альтернативные кодировки, но необходимо использовать ReadByte Write метод или и выполнить кодирование самостоятельно.
GetPortNamesМетод используется для получения допустимых портов для текущего компьютера.
Если SerialPort объект блокируется во время операции чтения, не прерывайте поток. Вместо этого закройте базовый поток или удалите SerialPort объект.
Конструкторы
Инициализирует новый экземпляр класса SerialPort.
Инициализирует новый экземпляр класса SerialPort, используя указанный объект IContainer.
Инициализирует новый экземпляр класса SerialPort, используя указанное имя порта.
Инициализирует новый экземпляр класса SerialPort, используя указанное имя порта и скорость передачи в бодах.
Инициализирует новый экземпляр класса SerialPort, используя указанное имя порта, скорость передачи в бодах и бит четности.
Инициализирует новый экземпляр класса SerialPort, используя указанное имя порта, скорость передачи в бодах, бит четности и биты данных.
Инициализирует новый экземпляр класса SerialPort, используя указанное имя порта, скорость передачи в бодах, бит четности, биты данных и стоп-бит.
Указывает, что отсчет времени ожидания не производится.
Свойства
Возвращает базовый объект Stream для объекта SerialPort.
Возвращает или задает скорость передачи для последовательного порта (в бодах).
Получает или задает состояние сигнала разрыва.
Возвращает число байтов данных, находящихся в буфере приема.
Получает число байтов данных, находящихся в буфере отправки.
Возвращает значение, показывающее, может ли компонент вызывать событие.
Получает состояние линии обнаружения несущей для порта.
Возвращает объект IContainer, который содержит коллекцию Component.
Возвращает состояние линии готовности к приему.
Возвращает или задает стандартное число битов данных в байте.
Возвращает значение, указывающее, находится ли данный компонент Component в режиме конструктора в настоящее время.
Возвращает или задает значение, показывающее, игнорируются ли пустые байты (NULL), передаваемые между портом и буфером приема.
Получает или задает состояние сигнала готовности данных (DSR).
Получает или задает значение, включающее поддержку сигнала готовности терминала (DTR) в сеансе последовательной связи.
Получает или задает кодировку байтов для преобразования текста до и после передачи.
Возвращает список обработчиков событий, которые прикреплены к этому объекту Component.
Возвращает или задает протокол установления связи для передачи данных через последовательный порт с использованием значения Handshake.
Возвращает значение, указывающее открытое или закрытое состояние объекта SerialPort.
Возвращает или задает значение, используемое для интерпретации окончания вызова методов ReadLine() и WriteLine(String).
Возвращает или задает протокол контроля четности.
Возвращает или задает байт, которым заменяются недопустимые байты потока данных при обнаружении ошибок четности.
Получает или задает последовательный порт, в частности, любой из доступных портов COM.
Возвращает или задает размер входного буфера SerialPort.
Получает или задает срок ожидания в миллисекундах для завершения операции чтения.
Возвращает или задает число байтов во внутреннем входном буфере до возникновения события DataReceived.
Возвращает или задает значение, показывающее, включен ли сигнал запроса передачи (RTS) в сеансе последовательной связи.
Получает или задает ISite объекта Component.
Получает или задает стандартное число стоповых битов в байте.
Возвращает или задает размер выходного буфера последовательного порта.
Получает или задает срок ожидания в миллисекундах для завершения операции записи.
Методы
Закрывает соединение порта, присваивает свойству IsOpen значение false и уничтожает внутренний объект Stream.
Создает объект, который содержит всю необходимую информацию для создания прокси-сервера, используемого для взаимодействия с удаленным объектом.
Удаляет данные из буфера приема последовательного драйвера.
Удаляет данные из буфера передачи последовательного драйвера.
Освобождает все ресурсы, занятые модулем Component.
Освобождает неуправляемые ресурсы, используемые объектом SerialPort, а при необходимости освобождает также управляемые ресурсы.
Определяет, равен ли указанный объект текущему объекту.
Служит хэш-функцией по умолчанию.
Извлекает объект обслуживания во время существования, который управляет политикой времени существования данного экземпляра.
Получает массив имен последовательных портов для текущего компьютера.
Возвращает объект, представляющий службу, предоставляемую классом Component или классом Container.
Возвращает объект Type для текущего экземпляра.
Получает объект службы времени существования для управления политикой времени существования для этого экземпляра.
Создает неполную копию текущего объекта Object.
Создает неполную копию текущего объекта MarshalByRefObject.
Открывает новое соединение последовательного порта.
Считывает из входного буфера SerialPort определенное число байтов и записывает их в байтовый массив, начиная с указанной позиции.
Считывает из входного буфера SerialPort определенное число символов и записывает их в символьный массив, начиная с указанной позиции.
Считывает из входного буфера SerialPort один байт в синхронном режиме.
Считывает из входного буфера SerialPort один символ в синхронном режиме.
Считывает все непосредственно доступные байты в соответствии с кодировкой из потока и из входного буфера объекта SerialPort.
Считывает данные из входного буфера до значения NewLine.
Считывает из входного буфера строку до указанного значения value .
Возвращает объект String, содержащий имя Component, если оно есть. Этот метод не следует переопределять.
Записывает указанное число байтов в последовательный порт, используя данные из буфера.
Записывает указанное число символов в последовательный порт, используя данные из буфера.
Записывает указанную строку в последовательный порт.
Записывает указанную строку и значение NewLine в выходной буфер.
События
Указывает, что данные были получены через порт, представленный объектом SerialPort.
Возникает при удалении компонента путем вызова метода Dispose().
Указывает, что произошла ошибка с портом, представленным объектом SerialPort.
Указывает, что для порта, представленного объектом SerialPort, возникло событие сигнала, не связанного с данными.
Сограждане, применяющие микроконтроллеры, часто испытывают потребность использования функций обмена по интерфейсу RS-232. Для этого надо уметь программировать и писать обслуживающие программы уже не только для микроконтроллеров, а и для персональных компьютеров.
В приложении - готовый, написанный мною открытый проект, который можно использовать в своих работах как образец-заготовку. Проект небольшой, так что его легко будет освоить, затем дополнить и расширить его функциями, в общем, использовать по своему усмотрению.
Содержание / Contents
При запуске программы PR14_RS-232.exe непосредственно или через оболочку MSVS2010 появится следующее основное окно, что на следующем рисунке.Все принятые коды выводятся в аналогичном формате в окне «Принятый код», при этом размер принятого кода может быть достаточно большим, и в этом случае дополнительно появляется полоса прокрутки.
То есть используя данную программу можно обмениваться через СОМ1 со скоростью 9600 бит/с, что вполне достаточно для многих приложений при работе с микроконтроллерами.
↑ А что же внутри программы, каков исходный код?
Основная часть написанной мною программы для проекта PR14_RS-232.sln содержится в файле Form1.cs.Рассмотрим некоторые особенности и функции кода в этом файле Form1.cs.
В самом начале файла, основные подключения через директивы using, без которых ничего работать не будет. Последние из них, что с комментариями, необходимы для работы с СОМ портом и таймером. По поводу таймера, СОМ порт может обходиться и без него, таймер добавлен для последующего улучшения пользовательского интерфейса.
Создание СОМ порта обеспечивают следующие строки в class Form1. Здесь же видно, что обмениваться данными будем через СОМ1, но легко можно задать и другие.
Кстати, если скорость и другие параметры не устраивают, то их меняем подобно следующему:
В основной функции public Form1() четыре строки. Первая формирует основную форму с кнопками и окошками, это стандартная строка, которая всегда создается автоматически. Следующие две строки создают обработчик прерываний для обменов по СОМ порту и отрывают сам СОМ порт. Четвертая строка выводит шаблон из шести байт в окно выдачи, к которому можно сделать привязку и, конечно, изменить.
В обработчике приема void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) имеется две строки, одна в самом начале, а другая в конце. Эти строки необходимы для синхронизаций, их обязательно надо применять. Управление передается обработчику, как только во входном буфере СОМ порта появляются принятые данные. Для считывания данных применяется следующие три строки. Здесь принятые коды считываются в байтовый буфер buffer.
Очередные следующие за ними три строки выводят данные в окно «Принятый код». Таким образом, в этом обработчике осуществляется прием по RS-232 и побайтный вывод принятого кода, а между байтами вставляются пробелы, что улучшает их обзор.
Где в последней строке, bytes – это сам подготовленный массив байт для выдачи, а bytes.Length – число выдаваемых в СОМ порт байт. Данный обработчик использует еще функцию private byte STB(char ch), служащую для преобразования знаковых символов в числа.
При нажатии кнопок «Очистить» работают обработчики private void button3_Click(object sender, EventArgs e) и private void button2_Click(object sender, EventArgs e), основная задача которых очистка окон приема и выдачи.
Вот такая небольшая программка.
↑ Файлы
↑ Немного о себе
Я занимаюсь схемо-техникой с применением микроконтроллеров. Их было у меня достаточно много: это Intel 8080, 8051, 196, Atmel AT90, AVR, ATtiny и megaAVR, Silabs, а также NiosII. Их я программировал на ассемблере и С, и связывал с персональным компьютером по интерфейсу RS-232 на подобных программах, ну и с более сложным функционалом.
Первый пост данного раздела посвящен COM порту и асинхронному режиму приема передачи данных по COM порту. Асинхронный режим COM порта примечателен тем, что позволяет принимать для дальнейшей обработки данные побайтно, что в свою очередь дает возможность работать с пакетами байт переменной длины.
Допустим стоит задача принять на COM порт пакет данных количеством от 800 до 4000 байт с последующим ответом в 128 байт с периодом обмена данными 1 секунда. Начало пакета имеет синхробайты 0xA1 0xA2. Как нельзя лучше для такой задачи подойдет асинхронный режим. Запустить такой режим можно в потоке используя известные функции работы с COM портом. Ниже по тексту представлен небольшой проект Visual Studio MFC в котором данная задача решена используя всего одну нить или проще говоря поток. Архив с проектом для Visual Sdudio 2008 (MFC) в конце данного поста. Тестирование осуществляется с помощью общеизвестной бесплатной программы монитора COM порта ComPortToolkit.
В архиве с проектом имеется текстовый файл MFCtest.txt в котором записана строка из 850 байт информации передаваемой на COM порт. Для тестирования необходимо чтобы на ПК имелось как минимум 2 COM порта соединенных перекрестным кабелем (выход одного COM подан на вход другого).
Один нюанс. Дело в том, что вход в функцию потока приема-передачи операционная система на разных ПК, будет осуществлять по разному. Имеется ввиду то, что цикл входа в поток на разных ПК будет иметь разный период в зависимости от скоростных характеристик самого ПК. Это означает, что в буфере приема при каждом очередном входе в поток может быть как один байт, так и несколько. Поэтому для того, чтобы не нарушался обмен необходимо правильно выставлять задерку приема в выпадающем списке 'Задержка', которая даст возможность подождать пока в приемный буфер будет загружен весь непрерывный пакет инфрмации передаваемый из ComPortToolkit. Для скорости обмена в 115200 бод и длины строки приема в 850 байт достаточна задержка в 100 мс. Можно обойтись и без задержки. Тогда программу, при необходимости, вполне можно модифицировать на прием информации порциями появляющимися в приемном буфере при каждом входе в функцию потока, с накоплением данных в отдельном буфере и формированием ответа по появлению в приемном буфере заранее известных конечных символов принимаемого пакета данных. Дерзайте.
Надеюсь, вы еще пали духом в освоении COM порта. Все не так уж и мрачно. Некоторые результаты можно получить и без USART. Сформулируем задачу, которую реализуем на начальном этапе работы с COM портом:
"Хочу что бы к компьютеру через COM порт подключался светодиод. Запускаю программу. Далаю какое-то действие в этой программе, светодиод загорается, делаю другое - светодиод тухнет."
Задача довольно специфичная (с учетом того, что USART не используется) и является чистой "самопальщиной", но вполне реализуема и работоспособна. Давайте приступим к ее реализации.
Опять берем системный блок вашего ПК и смотрим в тыловую часть. Примечаем там 9-ти штырьковй разъем - это и есть COM порт. Реально их может быть неколько (до 4-х). На моем ПК установлено два COM порта (см. фото).
2. Удлинитель COM порта
Нам потребуется удлинитель COM порта, но не просто удлинитель, а т.н. нуль-модемный кабель. Как он выглядит? Так как на фото, причем на обох его концах установлен разьем типа "мама". Далее втыкайте его в любой COM порт Вашего ПК и выводите его на рабочее место.
3. Аппаратная часть
С аппаратной частью нам тоже придется "повозиться", в том смысле что она будет сложнее чем с первым устройством для LPT порта. Дело в том что протокол RS-232 по которому идет обмен данными в COM порту, имеет несколько отличное соотношение логическое состояние - напряжение. Если обычно это логический 0 0 В, логическая 1 +5 В, то в RS-232 это соотношение следующее: логический 0 +12 В, логическая 1 -12 В.
И например, получив -12 В не сразу понятно что с этим напряжением делать. Обычно проводят преобразование уровней RS-232 в ТТЛ (0, 5 В). Самый простой вариант - стабилитроны. Но я предлагаю сделать этот преобразователь на специальной микросхеме. Называется она MAX232.
Микросхема эта дешевая и широко распространенная. Особенных проблем в ее добывании возникнуть не должно. Что она умеет? Она одновременно может преобразовывать два сигнала из RS-232 в ТТЛ и еще два из ТТЛ в RS-232.
Теперь давайте посмотрим, а какие сигналы из COM порта мы можем посмотреть на светодиодах? В действительности, в COM порту есть аж 6 независимых линий, представляющих интерес для разработчика устройств сопряжения. Две из них пока для нас недоступны - линии по передаче последовательных данных. А вот оставшиеся 4 предназначены для управления и индикации процесса передачи данных и мы сможем "передалать" их под свои нужды. Две из них предназначены для управления со стороны внешнего устройства и мы их пока трогать не будем, а вот последние две оставшиеся линии мы сейчас и поиспользуем. Они называются:
- RTS - Запрос на передачу. Линия взаимодействия, которая показывает, что компьютер готов к приему данных.
- DTR - Компьютер готов. Линия взаимодействия, которая показывает, что компьютер включен и готов к связи.
Сейчас мы немного передалаем их назначение, и светодиоды, подключенные к ним будут либо тухнуть либо загораться, в зависимости от действий в нашей собственной программе.
Итак, давайте соберем схему, которая позволит нам проводить задуманные действия.
А вот ее практичекая реализация. Я думаю вы меня простите, что я сделал ее в таком стремном макетном варианте, ибо делать плату для такой "высоко продуктивной" схемы не хочется.
4. Программная часть
Тут все попроще. Давайте создадим Windows приложение в Microsoft Visual C++ 6.0 на основе MFC для управления двумя линиями взаимодействия COM порта. Для этого создаем новый проект MFC и указываем ему имя, например, TestCOM. Далее выбираем вариант построения на основе диалога.
Придайте внешний вид окну диалога нашей программы, как на рис. ниже, а именно добавьте четыре кнопки, по две на каждую из линий. Одна из них соответственно необходима для того чтобы "погасить" линию, другая чтобы ее "установить" в еденицу.
Далее, в файле TestCOMDlg.h в описание класса диалога добавьте строчку: HANDLE hFile;.
Чтобы наша программа могла упрявлять линиями COM порта, его надо сначала открыть. Напишем код, ответственный за открытие порта при загрузке программы.
В итоге, функция CTestCOMDlg::OnInitDialog(), расположенная в файле TestCOMDlg.cpp, класса нашего диалога должна принять вид:
Теперь добавим обработчики кнопок управления линиями. Я дал им соответствующие имена: функция, которая устанавливает еденицу на линии DTR - OnDTR1(), 0 - OnDTR0(). Для линии RTS соответственно аналогичным образом. Напомню, что обработчик создается при двойном щелчке на кнопке. В итоге, эти четыре функции должны принять вид:
Поясню немного как они работают. Как видно, внитри себя они содержат вызов одной и той же Win API функции EscapeCommFunction() с двумя параметрами. Первый из них - это хэндл (HANDLE) на открытый порт, второй - специальный код действия, соответствующий необходимому состоянию линии.
Читайте также: