Передача данных по COM-порту.

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
hclubmk
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 254
Зарегистрирован: 19.06.2009 (Пт) 14:23
Откуда: От-туда

Re: Передача данных по COM-порту.

Сообщение hclubmk » 08.05.2025 (Чт) 20:26

серверянин писал(а):Не стоит так завидовать.
Можете тоже попердеть в лужу - и будет Вам щастье :wink:
:mrgreen:
Научились ли Вы радоваться трудностям?

серверянин
Новичок
Новичок
 
Сообщения: 48
Зарегистрирован: 19.09.2023 (Вт) 8:00
Откуда: РФ

Re: Передача данных по COM-порту.

Сообщение серверянин » 08.05.2025 (Чт) 21:16

Ну, кто и пердит, а моим модулем, хоть он и неидеален, 698 человек уже пользуются. А Вы только язвить стараетесь, да и то не очень остро получается 8)

hclubmk
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 254
Зарегистрирован: 19.06.2009 (Пт) 14:23
Откуда: От-туда

Re: Передача данных по COM-порту.

Сообщение hclubmk » 08.05.2025 (Чт) 22:11

серверянин писал(а):698
я предложил тебе профессиональное решение, а ты ведешь себя как баттхёрт. Попробуй повзрослеть.
Научились ли Вы радоваться трудностям?

серверянин
Новичок
Новичок
 
Сообщения: 48
Зарегистрирован: 19.09.2023 (Вт) 8:00
Откуда: РФ

Re: Передача данных по COM-порту.

Сообщение серверянин » 08.05.2025 (Чт) 22:35

hclubmk писал(а): я предложил тебе профессиональное решение

Если это про тот драйвер времен WIN9x, то спасибо, не надо. У меня другой подход.

hclubmk
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 254
Зарегистрирован: 19.06.2009 (Пт) 14:23
Откуда: От-туда

Re: Передача данных по COM-порту.

Сообщение hclubmk » 08.05.2025 (Чт) 23:12

серверянин писал(а):Если это про тот драйвер времен WIN9x
ну-ка, ну-ка, расскажи мне про что-то новое в работе с com?
серверянин писал(а):У меня другой подход.
ага, через задний проход. Не позорься уже, и иди учить уроки, баттхёрт :mrgreen:
больше ответов не будет, с пассажиром серверянин всё плохо, просмотрел его посты - просто клиника.
Научились ли Вы радоваться трудностям?

серверянин
Новичок
Новичок
 
Сообщения: 48
Зарегистрирован: 19.09.2023 (Вт) 8:00
Откуда: РФ

Re: Передача данных по COM-порту.

Сообщение серверянин » 08.05.2025 (Чт) 23:48

hclubmk писал(а): ага, через задний проход. Не позорься уже, и иди учить уроки, баттхёрт :mrgreen:
больше ответов не будет, с пассажиром серверянин всё плохо, просмотрел его посты - просто клиника.
Ууууу, какой вонючей трольчатинкой понесло - аж до самого Киева.
Неее, я в такое не полезу, не моё амплуа.

hclubmk
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 254
Зарегистрирован: 19.06.2009 (Пт) 14:23
Откуда: От-туда

Re: Передача данных по COM-порту.

Сообщение hclubmk » 08.05.2025 (Чт) 23:49

Просьба к администрации форума удалить учетную запись пьяного подростка!
Научились ли Вы радоваться трудностям?

серверянин
Новичок
Новичок
 
Сообщения: 48
Зарегистрирован: 19.09.2023 (Вт) 8:00
Откуда: РФ

Re: Передача данных по COM-порту.

Сообщение серверянин » 09.05.2025 (Пт) 11:36

Итак, проигнорив тролля, продолжу по делу.

Бодания с искусственным интеллектом всё же не прошли бесполезно. Его советы помогли мне создать новую версию модуля для работы с COM-портами, которую выкладываю здесь ниже. По сравнению с предшествующей версией, здесь два главных улучшения.
Оформление в виде класса позволяет открывать несколько портов одновременно, а не один, как было в старой версии.
Во-вторых, преодолено ограничение на 9 номеров COM-а. Новый модуль открывает все номера. Я не мог это проверить на всех существовавших версиях Windows; возможно на очень древних может остаться проблема с двузначными номерами, но начиная с XP всё уже работает исправно.
Кроме того добавлен мелкий сервис, типа списка имеющихся портов.
Вызывать событие впрямую аппаратным прерыванием не получилось. Думаю, что в VB такое вряд ли возможно. Но удалось довольно похоже имитировать это через быстрый опрос в фоновом цикле. Но сразу же выяснилась странная заморочка: DoEvents почему-то не обрабатывает CheckBox-ы, поэтому во время цикла они перестают реагировать. Впрочем, все остальные кнопки продолжают нормально работать.
Хотя событийная модель теперь возможна, но практика показала, что вполне можно обойтись без использования событий, и в некоторых случаях даже удобнее, что я и рекомендую.

Модуль ориентирован на работу с устройствами, подключенными к последовательному порту (как например Ардуино). Обмен, как правило, происходит в такой последовательности: программа посылает устройству команду из одного или нескольких байт, устройство что-либо делает и посылает отчет о своем новом состоянии, и/или другие данные в ответ.
Отсюда происходят некоторые особенности, с которыми я столкнулся в использовании старого модуля.
Передача с компьютера происходит аппаратно, то есть программа бросает данные в буфер передачи и сразу же переходит к приему ответа. А устройство еще не начало отвечать, потому что даже команда ему еще до конца не передалась. Приходилось везде ставить задержки.
Теперь этого не нужно, я сделал передачу с контролем окончания передачи и прием с ожиданием прихода всех символов. Так что глобальное непрерывное ожидание теперь не очень-то и нужно.

Вот список свойств и методов, которые дает модуль.

.OpenCOM -открытие порта. Аргументом имеет номер порта числового типа (а не строкового), чтобы легко было перебирать в цикле. За ним возможны несколько необязательных аргументов: скорость (по умолчанию 9600), битность, четность и стопы - по умолчанию стандартный режим 8, N, 1. (Лично мне ни разу не потребовался другой).
Если порт открыт - возвращает True, в противном случае False, так что открытие порта можно ставить непосредственно в If, проверяя таким образом доступность порта, и одновременно сразу же открывая его тем самым.
Повторное открытие уже открытого порта не является ошибкой. Порт автоматически корректно закрывается и затем открывается вновь, возможно с другими параметрами и номером.

.ClosePort -закрытие порта. Обязательно должно быть выполнено перед выходом из программы. Поставить в Form_Unload. Попытка закрытия уже закрытого или не открытого порта допустима и не приводит к ошибкам. Аргументов не имеет и ничего не возвращает.

.WritePort - простая передача строки в порт. Аргумент - строка символов. Не возвращает ничего.
.WritePortWait -то же самое, но выполнение приостанавливается и продолжается после фактического завершения передачи всех байтов.

.ReadPort -прием из порта. Имеет необязательный аргумент: количество принимаемых символов. Если аргумент не указан, считывает всё, что на текущий момент было в буфере приема. Возвращает принятые данные в виде символьной строки. Не выполняет ожидания. Если никаких данных не было принято, возвращает пустую строку сразу.
.WaitReadPort -перед началом приема ожидает время, рассчитанное по количеству символов и скорости приемопередачи. Аргумент - количество принимаемых символов. Аргумент обязательный, так как невозможно сделать расчет для неопределенного количества. Чтобы прочесть все принятые байты, можно получить их количество, используя свойство InBytes. Если данных нет, возвращает пустую строку.
.LineReadPort -прием строки, завершенной кодами перевода строки (CrLf). Имеет аргументом таймаут в секундах (против зависания, если перевод строки не появился). Если аргумент нулевой, то прием без ожидания.
Возвращает строку данных с кодом CrLf включительно. Если CrLf нет, возвращает то, что есть.

В случае ошибки все функции устанавливают свое сообщение об ошибке в свойство .LastError, где оно может быть прочитано (кириллица).
Если после выполнения любого из действий ошибки не было, это свойство содержит пустую строку.
Если подключены события, то при ошибках записи вызывается событие _WriteFault
а при ошибках чтения- _ReadFault
В обоих передается строка ErrMsg содержащая то же, что и .LastError .
Открытие порта этих событий не вызывает, так как возвращает результат выполнения в булевском формате.

.InBytes - свойство отображает количество пришедших байтов в буфере приема. Только для чтения.

.WaitData - ожидание данных.
После кода приравнивания этому свойству значения True, выполнение приостанавливается, входя в цикл бесконечного ожидания прихода данных. Приходом первого же байта в порт, этот цикл завершается и выполнение команд продолжается дальше.
.WaitData не считывает данных, а только ожидает факта их прихода. Считывать после нее любой из команд чтения порта.
Если подключены события, то .WaitData вызывает событие _DataReady . С этим вариантом ожидание можно запустить глобально сразу при запуске программы. Но делать это следует не в Form_Load, так как выполнение приостанавливается и форма тогда не сможет загрузиться.
Запускать ожидание глобально следует в самом конце Form_Activate. Как это правильно сделать можно посмотреть в коде прилагаемой тестовой программы; там события реализованы.
Ожидание можно прервать, установив свойству .WaitData значение False.

.PurgeData -очищает все данные и все очереди. Предназначено для защиты от случайно сохранившихся данных, могущих вызвать помехи для программы. Аргументов не имеет и ничего не возвращает.

.ListPorts -массив, содержит список номеров портов, имеющихся в системе. Начальный элемент массива (с индексом 0) содержит общее количество портов и соответствует размеру массива. Если он равен нулю, значит COM-портов на машине нет.
Остальные элементы массива содержат номера портов. Только для чтения.

.Tag -свободная переменная универсального типа, не участвующая в выполнении. Предназначена для какого-нибудь временного хранения или передачи данных между подпрограммами.

.hPort - дескриптор порта (R/O).


Лицензия: опенсорс :) AS IS, что мог, то сделал; кому не нравится - пусть сделает лучше и выложит здесь ко всеобщему удовольствию :wink:
Вложения
clscomm.zip
Модуль класса для работы с COM-портами в VB6
(9.97 Кб) Скачиваний: 115

серверянин
Новичок
Новичок
 
Сообщения: 48
Зарегистрирован: 19.09.2023 (Вт) 8:00
Откуда: РФ

Про тормоза, многопоточность и DDE

Сообщение серверянин » 03.03.2026 (Вт) 16:58

Довольно долго я использовал свой модуль commctl.cls (который здесь выложен выше) без особенных проблем, и вот только недавно выяснилась неприятная заморочка.
Ситуация следующая.
К COM-порту подключено внешнее устройство, выдающее периодические сигналы, на которые программа должна отвечать. И вот тут обнаружилось, что если окно программы начать просто двигать мышкой по экрану, то работа программы останавливается, пока мышка не будет отпущена. Соответственно, программа перестает отвечать устройству (со всеми вытекающими). До этих пор компьютер всегда выступал в ведущей роли, и, так как порт буферизован, тормоза были незаметны.
Нет, ну я понимаю, что выполнение стопорится, когда выдаются окна сообщений, потому что они модальные, но чтоб ухват окна мышкой останавливал выполнение - это уж совсем никуда не годится!.. Но обойти эту неприятность никак не удавалось, потому что так работает Бейсик, и с этим ничего не поделаешь, либо нужна не имитированная, а настоящая многопоточность, которой занимался уважаемый The trick, но у меня нет такого уровня знаний, чтоб пойти по его стопам.
Поэтому я решил попросту вынести критичные процедуры в отдельно запущенный процесс, связав его с основной программой при помощи механизма DDE.
Однако мне не хотелось таскать вместе со скомпилированным экзешником еще дополнительный файл (тем более исполняемый), и я заставил программу при старте запускать саму себя повторно, создавая тем самым второй экземпляр (скрытый), в котором я и сосредоточил работу с COM-портом.
Код был распланирован для работы в двух режимах: в основном (работа видимого окна с пользовательским интерфейсом) и вторичном (второй экземпляр, невидимый на экране), в котором производятся только быстрые обработки. Передачу между ними я задумал сделать по DDE.

Несколько слов про DDE в Бейсике
DDE (Dynamic Data Exchange) позволяет создавать связь между программой-источником и программой-приемником. Связь в общем двунаправленная, но управляющая ею сторона называется приемником.
Чтобы сделать программу-источник, достаточно установить форме свойство LinkMode=Источник (остальное по умолчанию). Тогда все объекты на этой форме, имеющие свойства Link..., будут доступны для обращений по DDE. Но устанавливать форме это свойство надо в проекте еще до компиляции, потому что такая форма при загрузке резервирует дополнительную память, так что превратить форму в источник во время работы нельзя. (Впрочем, обратно в LinkMode=0 -можно).
В программе-приемнике мы устанавливаем связи отдельно каждого желаемого объекта с указанным объектом источника. Для указания служит свойство принимающего объекта LinkTopic, содержащее строку: название приложения-источника и имя его формы, разделенные вертикальной чертой "|".
Трудно даже описать, какое жестокое количество непроходимых гиморов я преодолел, пока выяснил, что "название источника" - это App.Title, а не "имя файла без расширения exe", как утверждается в имевшихся у меня пособиях, и задается это название в "Свойствах" проекта на вкладке "Создание" (где устанавливается иконка приложения).
Ну а дальше всё просто, это легко посмотреть в коде: сначала LinkMode=vbLinkNone, потом задаем LinkTopic, потом LinkItem = имя объекта-источника, и включаем связь: LinkMode=vbLinkAutomatic . В этом режиме изменения объекта-источника будут автоматически отображаться на объекте-приемнике (а в режиме LinkMode=vbLinkManual нужен запрос командой).
Обратная передача от источника к приемнику делается использованием метода LinkPoke (автоматически этого не происходит).

Теперь предметно по коду приложенной демошки.
Как уже упомянул, программа запускает второй экземпляр себя (вторичное окно) при помощи директивы Shell. Таким образом получается полностью независимый параллельный процесс.
Это потребовало немного навороченного манипулирования окнами, но в сущности ничего сверхъестественного там нет. Подключены четыре простых API-функции, которые отыскивают и управляют окнами по их заголовкам. Для этого основное и вторичное окно различаются заголовками, а все остальные имена между ними одинаковы (так как это экземпляры).
Для простоты я соединял по DDE каждый выбранный объект с его же копией в другом окне (хотя ничто не мешало соединять иначе). Поэтому использовались свои же имена.
Списки Combo нельзя подключить по DDE, потому что у них нет свойств Link. Данные списков передаются через метки (Label), у них эти свойства есть. Я сделал служебный массив из трех невидимых меток под именами DDE(0)..DDE(2). Если Вам понадобятся еще метки для передачи чего-нибудь, Вы можете легко добавить их просто скопировав любую из DDE-меток. При вставке на форму копия автоматически присоединится к массиву со следующим номером, и цикл в Form_Load правильно проинициализирует ее вместе с другими, Вам не понадобится заботиться об этом.
Вообще, Form_Load в основном независима от имен. Вы можете использовать любое имя приложения, любое имя формы и даже любое имя вторичного окна. Только с тем условием, чтобы не менять их во время выполнения.
Не следует также менять тег формы (Me.Tag), он является флагом режима. Если он пустая строка - значит мы находимся во вторичном экземпляре. А в основном режиме он содержит заголовок вторичного окна (используется при закрытии программы).
Единственные имена в Form_Load это Text1 (используется для отправки данных) и Text2 (для приема данных). Они являются примером, как поступать с ними.
Остальное лучше разбирать по комментам в коде.

Почему не видно на экране вторичное окно, хотя его свойство Visible=True ?
Потому что режим ожидания COM-порта запущен в Form_Load. В результате выполнение уходит в цикл ожидания данных не доходя до Form_Activate, и форма так и не появляется на экране. Теперь ее уже не может тормозить мышка.

Сложность состоит в том, что такая программа не запускается из среды Бейсика, ее надо обязательно скомпилировать в exe-файл. Об этом на всякий случай вставлено сообщение в начале Form_Load (разумеется, его надо убрать, если на основе этого кода демошки Вы будете делать что-нибудь свое).

Я даже соглашусь, если кто скажет, что это - гнусные извращения, несовместимые с традиционными ценностями. Но зато - оно работает, причем работает без каких-либо хакерских приемов, легальными средствами Бейсика и системы.
Вложения
DDE_COM.zip
(12.23 Кб) Скачиваний: 4

The trick
Постоялец
Постоялец
 
Сообщения: 813
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Передача данных по COM-порту.

Сообщение The trick » 05.03.2026 (Чт) 0:07

А почему бы не использовать этот класс?
Вызываем WaitCommEvent в асинхронном режиме. В метод vbWaitForSingleObject класса передаем событие которое установится когда данные придут. Далее ловим событие OnWait и вызываем ClearCommError чтобы узнать сколько в rx буфере данных. Через ReadFile читаем их и далее по кругу.
UA6527P

Пред.

Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

    TopList