Skip to content

Instantly share code, notes, and snippets.

@Alexflex
Last active September 11, 2024 13:09
Show Gist options
  • Save Alexflex/9995dee5965479e9d0e53e2a026eb9db to your computer and use it in GitHub Desktop.
Save Alexflex/9995dee5965479e9d0e53e2a026eb9db to your computer and use it in GitHub Desktop.
Лабораторная работа RS-232
title
Лабораторная работа RS-232

Разработка коммуникационного ПО для последовательного порта

Цель лабораторной работы

Целью лабораторной работы является знакомство с последовательным интерфейсом UART и его реализацией на ПК (COM-порт), а также получение навыков разработки и использования ПО для обмена данными с периферийным устройством.

Задачи лабораторной работы

  • изучение принципов работы старт-стопной синхронизации;
  • изучение принципов работы интерфейса RS-232;
  • изучение принципов проектирования коммуникационного ПО для обмена данными с удаленным устройство.

Задание на лабораторную работу

  1. Ознакомиться с основными элементами адаптера асинхронной связи и коммуникационного ПО.
  2. Изучить особенности программирования адаптера асинхронной связи для работы в различных режимах.
  3. Отладить и протестировать программы, реализующие алгоритмы, заданные преподавателем (см. пункт “Ход работы”).

1. Архитектура последовательного порта

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

1.1. Стандарт последовательной передачи RS-232

Данный стандарт был введен в действие организацией EIA (США) в 1969 г. под названием RS-232-C (Recommended Standard).

Стандарты RS-XXX являются условно стандартными, поскольку аббревиатура RS содержит “мягкое” слово “Recommended Standard” – “рекомендованный стандарт”. Эти рекомендации исходят от американской организации EIA, RS-232-подобный стандарт описан в ГОСТ 18145-81.

Первоначально протокол, описанный в стандарте, предназначался для подключения к аппаратуре, предназначенной для передачи данных (Data Terminal Equipment – DTE) устройств, отвечающих за непосредственное соединение с линией передачи (Data Communication Equipment – DCE). Полное название “Интерфейс между терминальным оборудованием и связным оборудованием с обменом по последовательному двоичному коду”. Каждое слово в названии значимое, оно определяет интерфейс между терминалом (DTE) и модемом (DCE) по передаче последовательных данных.

В качестве первого устройства, как правило, выступал компьютер, а в качестве второго – модем, соединенный с линией связи. В дальнейшем область применения протокола расширялась. Он использовался для подключения к компьютеру периферийных устройств прикладного назначения (принтер, сканер, мышь и др.), а также для связи компьютеров между собой.

В настоящее время основная область его применения – связь со специализированными устройствами автоматизации (счетчики электроэнергии, расхода ресурсов и т. п.).

Интерфейс RS-232C предусматривает передачу и приём информации по несимметричной линии, то есть сигнал передаётся относительно общего провода. Логической единице соответствует уровень -12 …-3 В, логическому нулю +3…+12 В.
Далее будем называть такое оборудование наиболее распространенными терминами – “интерфейс RS-232C”, или “последовательный асинхронный интерфейс”.

Последовательный интерфейс RS-232 – это промышленный стандарт для после­довательной двунаправленной асинхронной передачи данных между двумя устройствами на расстоянии до 15 метров.
Информация передается по проводам с уровнями сигналов, отличающимися от стандартных 5 В, для обеспечения большей устойчивости к помехам. Асинхронная передача данных осуществляется с установленной скоростью при синхронизации уровнем сигнала стартового импульса.

Стандартные физические порты, используемые промышленными сетями:

  • RS-232,
  • RS-422,
  • RS-485.

Подавляющее большинство сетей использует наиболее мощный по числу узлов в сети и наиболее скоростной по передаче данных порт RS-485.

RS-232 широко используется в промышленных средствах авто­матизации. Он обеспечивает соединение «точка к точке» между последовательным портом контроллера и приборами. Часто, за счет усовершенствования передатчика и кабеля, достигаются большие длина линии и скорость, чем зафиксировано в стандарте.

Популярность интерфейса RS-232C объясняется его универсальностью по диапазону скоростей передачи информации (от 50 до 115000 бит в секунду), “прозрачностью”, т.е. отсутствием запрещенных к передаче кодовых комбинаций, наличием специализированных БИС и ИС, на которых достаточно эффективно реализуется данный интерфейс, простотой конструкции соединительных кабелей.

Микросхемы, на основе которых строится интерфейс RS-232C, обычно называют универсальными асинхронными приемниками – передатчиками (universal asynchronous receiver transmitter или UART).

1.1.1 Основные параметры и характеристики RS-232

Обычно, ПК имели в своем составе два интерфейса RS-232C, которые обозначались COM1 и COM2. Была возможна установка дополнительного оборудования, которое обеспечивало функционирование в составе ПК четырех, восьми и шестнадцати интерфейсов RS-232C.

В современных ПК разъемы для последовательного интерфейса на корпусе системного блока отсутствуют. Как правило, они заменены на набор выводов прямо на материнской плате.

Работа коммуникационных портов реализована на универсальных асинхронных приемопередатчиках UART. Для СОМ порта компьютера используется 9-ти штырьковый разъем DE9p. В этом разъеме используется шесть сервисных сигналов и два канала обмена последовательными данными.

Интерфейс между терминалом (DTE) и модемом (DCE) по передаче последовательных данных
Устройства для связи по последовательному каналу соединяются кабелями с девятью или двадцатью пятью контактными разъемами типа DE-9, DB-25, CANNON 9, CANNON 25 и т.д:

  • D-образный 9-ти или 25-пиновый разъем типа male для DTE устройства
  • D-образный 9-ти или 25-пиновый разъем типа female для DCE-устройства.

изображение
Назначение линий в соответствии со стандартом RS-232 приведено в таблице:

Обозначение Расшифровка Описание
PG Protective Ground защитное заземление (экран)
TxD Transmitted Data данные, передаваемые DTE
RxD Received Data данные, принимаемые DTE
RTS Request To Send сигнал запроса передачи к DCE
CTS Clear To Send сигнал готовности DCE к приему данных от DTE
DTR Data Terminal Ready готовность DTE к приему данных от DCE
DSR Data Set Ready Сигнал готовности DCE к приему и передаче данных
DCD Data Carrier Detect DCE принимает данные от удаленного DCE (обнаружена несущая)
RI Ring DCE принимает сигнал вызова из линии
SG Signal Ground сигнальное заземление, нулевой провод

Нумерация контактов в разъемах:
изображение

Контакт Обозначение Направление Описание
1 SHIELD Shield Ground – защитная земля, соединяется с корпусом устройства и экраном кабеля
2 TXD -> Transmit Data – выход передатчика
3 RXD <- Receive Data – вход приемника
4 RTS -> Request to Send – выход запроса передачи данных
5 CTS <- Clear to Send – вход разрешения терминалу передавать данные
б DSR <- Data Set Ready – вход сигнала готовности от аппаратуры передачи данных
7 GND System Ground – сигнальная (схемная) земля
8 CD <- Carrier Detect – вход сигнала обнаружения несущей удаленного модема
9-19 N/C - -
20 DTR -> Data Terminal Ready – выход сигнала готовности терминала к обмену данными
21 N/C - -
22 RI <– Ring Indicator - вход индикатора вызова (звонка)
23-25 N/C - -

изображение

Контакт Обозначение Направление Описание
1 CD <- Carrier Detect
2 RXD <- Receive Data
3 TXD -> Transmit Data
4 DTR Data Terminal Ready
5 GND System Ground
6 DSR <- Data Set Ready
7 RTS Request to Send
8 CTS <- Clear to Send
9 RI <- Ring Indicator

1.1.2 Универсальный асинхронный приемник – передатчик (UART, УАПП)

Интерфейс UART (Uuniversal Asynchronous Rreceiver/Ttransmitter) так же можно отнести к разновидности интерфейса RS-232, с той разницей, что он является наиболее “полной” его разновидностью.

Скорость передачи данных по интерфейсу UART может достирать до 2764800 бит/с.
Принципиальными отличиями в структуре интерфейса UART является то, что если в интерфейс RS-232 значение “Стоп-бит” могло принимать значения 1; 1,5 и 2, то интерфейс UART имеет значения “Стоп-бит” только 1 и 2.
Дополнительно в интерфейсе UART добавлено понятие “Направление бит” и “Уровень холостого хода”. Направление бит означает какие биты информации передаются первыми – старшие (MSB) или младшие (LSB). Уровень холостого хода, в отличие от интерфейса RS-232, в котором при отсутствии передачи информации, уровень на шине принимает низкое значение или уровень логического нуля, в интерфейсе UART при отсутствии передачи информации, уровень на шине может принимать как низкое значение (уровень логического нуля), так и высокое значение (уровень логической единицы).

Уровни сигналов UART
UART использует уровни сигналов -12 В…+12 В. Зоной нечувствительности – отсутствие сигналов – считается напряжение -3 В…+3 В. При этом принимаемые/передаваемые данные инвертированы.

изображение
Рис. Уровни сигналов UART по стандарту RS-232c

Исходные состояния порта

  • порт не инициализирован – на всех линиях напряжения находятся в диапазоне -3 В…+3 В
  • режим ожидания – на всех линиях напряжение находится в диапазоне -3 В…-12 В

1.1.3 Передача данных через UART

При передаче данных символы передаются из буфера передатчика последовательно (первым пришел – первым вышел).

На рис. показан вид сигнала на информационной линии интерфейса RS-232C. Логической единице соответствует напряжение -12 В, а логическому нулю – +12 В.
изображение
Передача символов “0” “0” без паритета, с одним стоповым битом

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

Стоповый бит разделяет два переданных символа. При необходимости можно увеличить этот интервал до 2 стоповых битов, если конечное устройство не успевает разделять символы.

изображение
Передача символов “0” “0” с проверкой на четность (EVEN), с одним стоповым битом


Рассмотрим пример передачи через интерфейс передается числа 0101010b, соответствующее кодировке символа «U». Следует обращать внимание, что последовательность старших и младших бит при дальнейших преобразованиях должна быть соблюдена: «Бит 7»-«Бит 6»-«Бит 5»-«Бит 4»-«Бит 3»-«Бит 2»-«Бит 1»-«Бит 0».

изображение Сигнал на информационной линии интерфейса RS‑232C
изображение Отображаемый информационный сигнал

Количество стоповых бит может быть увеличено пользователем ПК до 2. Стартовый бит единственен.

На основании полученной эпюры напряжения можно определить логическую последовательность передаваемых информационных разрядов:

[0(01001110)1][0(10100110)1][0(00110110)1][0(10000110)1][0(10011110)1],

где [стартовый(младший…старший)стоповый] биты.

В дальнейшем принятую последовательность необходимо преобразовать в кодировку ASCII. Результатом проделанных действий является выделенное слово “relay”.

Следует учитывать, что передача начинается с младших бит, а заканчивается старшим битом!

1.1.4 Контроль четности

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

Для обеспечения контроля четности компьютер и устройство должны одинаково производить подсчет бита четности. То есть, определиться устанавливать бит при четном (even) или нечетном (odd) числе единиц. При контроле на четность биты данных и бит четности всегда должны содержать четное число единиц. В противоположном случае бит соответствует контролю на нечетность.

Часто в драйверах доступны еще две опции на четность: Mark и Space. Эти опции не влияют на возможность контроля ошибок. Mark означает, что устройство всегда устанавливает бит четности в 1, а Space – всегда в 0.

Обнаружение ошибок
Проверка на четность – это простейший способ обнаружения ошибок. Он может определить возникновение ошибок в одном бите, но при наличии ошибок в двух битах уже не заметит ошибок. Также такой контроль не отвечает на вопрос какой бит ошибочный. Другой механизм проверки включает в себя Старт и Стоп биты, циклические проверки на избыточность, которые часто применяются в соединениях Modbus.

Пример
В примере показана структура передаваемых данных со синхронизирующим тактовым сигналом. В этом примере используется 8 бит данных, бит четности и стоп бит. Такая структура также обозначается 8Е1.
изображение
Примечание: Тактовый сигнал – для асинхронной передачи это внутренний сигнал

Старт-бит
Сигнальная линия может находится в двух состояниях: включена и выключена. Линия в состоянии ожидания всегда включена. Когда устройство или компьютер хотят передать данные, они переводят линию в состояние выключено – это установка Старт-бита. Биты сразу после Старт-бита являются битами данных.

Стоп-бит
Стоп-бит позволяет устройству или компьютеру произвести синхронизацию при возникновении сбоев. Например, помеха на линии скрыла Старт-бит. Период между старт и стоп битами постоянен, согласно значению скорости обмена, числу бит данных и бита четности. Стоп-бит всегда включен. Если приемник определяет выключенное состояние, когда должен присутствовать стоп бит, фиксируется появление ошибки.

Установка Стоп-бита
Стоп бит не просто один бит минимального интервала времени в конце каждой передачи данных. На компьютерах обычно он эквивалентен 1 или 2 битам, и это должно учитываться программе драйвера. Хотя, 1 стоп бит наиболее общий, выбор 2 бит в худшем случае немного замедлит передачу сообщения.

(Есть возможность установки значения стоп бита равным 1.5. Это используется при передаче менее 7 битов данных. В этом случае не могут быть переданы символы ASCII, и поэтому значение 1.5 используется редко).

1.1.5 Ослабление электрических сигналов при передаче по линиям связи

Электрические сигналы после прохождения по кабелю ослабляются и искажаются. Ослабление растет с увеличением длины кабеля. Этот эффект сильно связан с электрической емкостью кабеля. По стандарту максимальная нагрузочная емкость составляет 2500 пФ. Типичная погонная емкость кабеля составляет 130 пФ, поэтому максимальная длина кабеля ограничена примерно 17 м.

1.1.6 Проблемы с источником питания

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

1.1.7 Преобразование уровней RS-232 в TTL уровень с помощью MAX232

Два типа устройств RS-232, 1488 и 1489, используются и сейчас. Это ранние представители стандарта. Устройства того времени запитывались мощными источниками питания, поскольку согласно стандарту RS-232 передатчики должны были обеспечивать минимальный +5 В сигнал низкого уровня и минимальный -5 В сигнал высокого уровня. Эти уровни сигналов обеспечивали устойчивость к помехам после передачи по проводам к приемнику. Но это требовало наличие двухполярного источника питания, и поэтому многие материнские платы включали в себя источник отрицательного напряжения исключительно для питания устройств типа 1488 или 1489.

Семейство микросхем MAX220-MAX249 линейных приемо-передатчиков предназначены для интерфейсов EIA/TIA-232E и V.28/V.24, особенно в устройствах, где отсутствуют напряжения ±12 В.

Альтернативная микросхема ICL232. Это сдвоенный приемо-передатчик соответствующая спецификациям RS-232C и V.28. Для питания ИС требуется только напряжение +5 В. Напряжения +10 В и -10 В преобразуются из 5 В при помощи двух емкостных преобразователях напряжения.

На приведенном рисунке для сравнения отображены сигналы TTL serial и RS-232, снятые при передаче значения одного байта.
изображение

1.1.8 Микросхема MAX232

MAX232интегральная схема, преобразующая сигналы последовательного порта RS-232 в сигналы, пригодные для использования в цифровых схемах на базе ТТЛ или КМОП технологий. MAX232 работает приемопередатчиком и преобразует сигналы RX, TX, CTS и RTS

Микросхема MAX232 быстро стала индустриальным стандартом. Многие разработчики используют ее, несмотря на то, что параметры микросхем с однополярным питанием значительно улучшились.
Конфигурация выводов MAX232 представлена на рис.
изображение
Полезно понимать, что происходит с уровнями напряжения. Когда схема MAX232 получает на вход логический «0» от ТТЛ, она преобразует его в напряжение от +3 до +15 В, а когда получает логическую «1» — преобразует её в напряжение от −3 до −15 В, и по тому же принципу выполняет обратные преобразования от RS-232 к ТТЛ.

Тип линии и логический уровень RS-232 Напряжение RS-232 Напряжение от ТТЛ к MAX232 или обратно
Линия данных, логический «0» от +3 В до +15 В 0 В
Линия данных, логическая «1» от −3 В до −15 В 5 В

1.1.9 Соединительные кабели

Все DTE-DCE кабели прямого соединения, контакты соединяются один к одному.
Кабели DTE-DTE и DCE-DCE – кросс-кабели.

  • DTE - DCE называется “прямой кабель”
  • DTE - DTE называется “нуль-модемный кабель”
  • DCE - DCE называется “Tail Circuit Cable”

Схема подключения DTE-устройства к DCE-устройству показана на рис. Однако, изменив порядок соединения линий, можно добиться соединения двух DTE-устройств, что позволяет образовать между ними канал передачи данных.

Так как большая часть линий стандарта требуется для синхронизации работы DTE и DCE, то при соединении двух устройств DTE (двух компьютеров) эти линии остаются незадействованными. Для корректной работы микросхемы порта необходимо их замыкание на соответствующие парные выводы на ближнем конце. Таким образом, данное соединение требует кабеля, состоящего как минимум из 7 линий.

В настоящее время наиболее широко используется упрощенный способ соединения, в которой используется трехпроводная линия связи (линия приема, передачи и сигнальной земли).
изображение
Схемы подключения устройств по стандарту RS-232: a) DTE-DCE, б) DTE-DTE (полный нуль-модемный кабель), в) DTE-DTE (упрощенный вариант)

1.1.10 Нуль-модемное соединение двух COM-портов

При таком соединении терминалы соединяются между собой непосредственно через СОМ-порты. Так как компьютеры обладают большой скоростью обработки данных, то синхронизировать их работу не нужно. Предполагается, что режим синхронизации обмена (Handshaking): 0-None, то есть сервисные сигналы не влияют на процедуры обмена данными. Для этого используется нуль-модемный кабель.
изображение
Нуль-модемный кабель для Handshaking = 0 (None)

Так как режим синхронизации обмена на СОМ портах может быть включен, то часто сервисные сигналы СОМ портов замыкают самих на себя, тем самым исключая их влияния на процедуру обмена.

изображение
Нуль-модемный кабель для любых режимов Handshaking

Можно использовать полный кабель, но при этом СОМ-порты должны быть настроены на аппаратную синхронизацию обмена. Данный режим используется, когда устройство не успевает перерабатывать информацию, полученную по СОМ-порту. Этот режим позволяет останавливать обмен данных на время обработки полученной информации.
изображение
Нуль-модемный кабель для аппаратного режима синхронизации Handshaking=2

системный блок ПК и два 9-ти штырьковых разъема

Нуль-модемный кабель. На обоих его концах установлен разъем типа “female”.

1.2 Программирование последовательного порта

1.2.1 Инициализация последовательного порта

При проведении инициализации порта коммуникации (“открытии”) устанавливаются все характеризующие его режим параметры:

  • длину слова,
  • число стоп-битов,
  • установку четности
  • скорость обмена.

Длина слова – это число битов, которое образует основную единицу данных. Помимо варианта, когда обмен ведется привычными порциями по 8 битов, возможно использование 7 битов, что достаточно для стандартных файлов ASCII (в которых все символы имеют коды, не превышающие ASCII 128), в то время как для передачи численных данных достаточно порций по 4 бита.

Важно учитывать следующие основные принципы обмена информацией по интерфейсу RS-232C:

  1. обмен данными обеспечивается по двум цепям, каждая из которых является для одной из сторон передающей, а для другой приемной;
  2. в исходном состоянии по каждой из этих цепей передается двоичная единица, т.е. стоповая посылка. Передача стоповой посылки может выполняться сколько угодно;
  3. передаче каждого знака данных предшествует передача стартовой посылки, т.е. передача двоичного нуля в течение времени, равного времени передачи одного бита данных;
  4. после передачи стартовой посылки обеспечивается последовательная передача всех разрядов знака данных, начиная с младшего разряда. Количество разрядов знака может быть 5, 6, 7 или 8;
  5. после передачи последнего разряда знака данных возможна передача контрольного разряда, который дополняет сумму по модулю 2 переданных разрядов до четности или нечетности. В некоторых системах передача контрольного разряда не выполняется;
  6. после передачи контрольного разряда или последнего разряда знака, если формирование контрольного разряда не предусмотрено, обеспечивается передача стоповой посылки. Минимальная длительность посылки может быть равной длительности передачи одного, полутора или двух бит данных.

Обмен данными по описанным выше принципам требует предварительного согласования приемника и передатчика по количеству используемых разрядов в символе, правилам формирования контрольного разряда и длительности передачи бита данных.

Последнее согласование обеспечивается путем стандартизации ряда скоростей: 50, 75, 100, 110, 200, 300, 600, 1200, 2400, 4800, 9600, 19 200, 38 400, 57 000 или 115 000 бит в секунду. Установленная скорость должна отличаться от номинальной не более чем на 2 %, что гарантированно обеспечивается применением генераторов с кварцевыми резонаторами.

1.2.2 Взаимодействие с COM-портом в ОС Windows с помощью WIN API

ОС Windows зарезервировала имена от СОМ1 до СОМ9 для работы с физическими СОМ-портами.

Правильнее обращаться с последовательными портами через встроенные в Windows функции WinAPI (API Application Programming Interface, интерфейс программирования приложений). Их полное описание можно найти в справочной системе MS SDK Help files, поставляемой со многими системами разработки приложений. Функции для работы с коммуникационным оборудованием описаны в разделе Communications файла Win32 Programmer’s Reference.

Любые коммуникационные ресурсы, физические и логические устройства ввода/вывода с точки зрения Windows – одиночные двунаправленные асинхронные потоки данных. Их открывают, инициализируют и конфигурируют функцией CreateFile, закрывают функцией CloseHandle.

Для хранения параметров коммуникационных ресурсов определен тип tDCB (Device Control Block блок управления устройством) – структура, в полях которой задают скорость обмена, число бит в посылке, число стоп-бит, режим проверки на четность.
Предусмотрены функции GetCommState и SetCommState, которыми читают и устанавливают эти параметры. Размер буферов ввода и вывода задают функцией SetupComm.

Для чтения и записи в порт вызывают функции ReadFile, ReadFileEx, WriteFile, WriteFileEx.

Структура типа tCOMSTAT имеет поля cblnQueue и cbOutQueue, содержащие сведения о числе принятых байтов в буфере приема и числе еще не переданных в буфере передачи. Функция ClearCommError обновляет эту информацию и сообщает о возникших в процессе работы порта ошибках.

Ядро Windows самостоятельно обрабатывает все события, возникающие при работе последовательных портов, настраивает прерывания и программирует контроллеры портов ввода/вывода, принимает и передает байты данных. Прикладной программе остается читать принятые данные из буфера приемника, а предназначенные для передачи – записывать в буфер передатчика. Эти операции обычно выполняют блоками с периодичностью, зависящей от скорости приема/передачи и объема буферов.

Последовательность операций работы с COM-портом
Работа с портом средствами функций WinAPI аналогична работе с файлом. Для работы создается идентификатор открытого порта (хэндл).

HANDLE hPort;

Функция для открытия порта:

LPCTSTR Name=L"COM1"; 		// UNICOD L""
hPort = CreateFile(	Name, 
			GENERIC_READ | GENERIC_WRITE, 
			0, 
			0, 
			OPEN_EXISTING, 
			FILE_ATTRIBUTE_NORMAL, 
			0);
//здесь выполняем действия, требуемые в случае неуспешного открытия порта
if (hPort == INVALID_HANDLE_VALUE)
{
	DWORD dw = GetLastError();
	FormatMessage( 	FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			dw,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR)&lpMsgBuf,
			0, NULL);
	message = Convert::ToString(lpMsgBuf);
	MessageBox::Show(this, message, "Ошибка", MessageBoxButtons::OK);
	return;
}
//здесь выполняем действия, требуемые в случае успешного открытия порта
...

Инициализация параметров порта выполняется заданием значений полей структуры DCB. Работа с данной структурой:

DCB dcbSerialParams = { 0 };
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (!GetCommState(hPort, &dcbSerialParams))
{
	MessageBox::Show(this, "Получение свойств порта", "Ошибка", MessageBoxBut-tons::OK);
	return;
}
dcbSerialParams.StopBits = ONESTOPBIT; 	// количество стоп-битов
					// ONESTOPBIT - 1 стоп-бит
					// ONE5TOPBITS - 1.5 стоп-бита
					// TWOSTOPBITS - 2 стоп-бита
dcbSerialParams.BaudRate = CBR_1200; 	// скорость подключения (бод/с)
dcbSerialParams.ByteSize = 5; 		// количество информационных бит
dcbSerialParams.Parity = NOPARITY; 	// режим проверки на четность
					// NOPARITY – нет проверки,
					// EVENPARITY – проверка на четность
					// ODDPARITY – проверка на нечетность
if (!SetCommState(hPort, &dcbSerialParams))
{
	MessageBox::Show(this, "Запись свойств порта", "Ошибка", MessageBoxButtons::OK);
}

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

COMMTIMEOUTS CommTimeOuts = { 0xFFFFFFFF,0,0,0,1500 };
if (!SetCommTimeouts(hPort, &CommTimeOuts))
{
	MessageBox::Show(this, "Запись таймаутов порта", "Ошибка", MessageBoxBut-tons::OK);
}

В случае успешного подключения к порту можно принимать и передавать данные с помощью функций ReadFile и WriteFile.
Перед вызовом метода WriteFile необходимо сформировать массив байтов для передачи. Например, если требуется передать слово “Hello”, вызов метода будет иметь вид:

char str[10]; 		// строка для передачи
DWORD dwSize = 5; 	// размер строки
DWORD dwBytesWritten; 	// количество переданных байт
strcpy(str,”Hello”);
BOOL iRet = WriteFile(hPort, str, dwSize, &dwBytesWritten, NULL);
if (dwBytesWritten!= dwSize)
{
	MessageBox::Show(this, "Ошибка при записи в порт", "Ошибка", MessageBoxBut-tons::OK);
}

Функция для получения данных считывает данные побайтно:

DWORD iSize;
char sReceivedChar[2];
char recBuf[100];
string Symb;
sReceivedChar[1] = 0;
do
{
	ReadFile(hPort, &sReceivedChar, 1, &iSize, 0); 	// получаем 1 байт
	// если что-то принято, выводим
	if (iSize > 0) 						
	{
		strcat(recBuf, sReceivedChar);
	}
} while (iSize > 0);

считывает все данные, принятые из последовательного интерфейса и сохраненные в буфере приема recBuf.
При завершении работы с портом необходимо закрыть подключение вы-зовом метода CloseHandle.

CloseHandle(hPort);

Обработка событий COM-порта

дописать про асинхронную обработку и многопоточный режим
по материалам PCPORTS
Агуров, П. Последовательные интерфейсы ПК. Практика программирования / П. Агуров. – СПб.: БХВ-Петербург, 2004. – 496 с.
и по std::thread c++

Для того чтобы узнать о том, что произошло изменение сигнала на линии CTS или DSR, можно применить два подхода:

  • с помощью таймера (периодический опрос состояния линий). Недостаток – минимальный интервал опроса составляет всего 10-20 мс
  • использование специализированных функций-обработчиков событий для COM-порта

Функция SetCommMask указывает стандартному драйверу порта отслеживать определенные события. Становится возможным узнать о каждом случае возникновения события порта.

Пример листинга

HANDLE hCom;
OVERLAPPED o;
BOOL fSuccess;
DWORD dwEvtMask;

hCom = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, /* exclusive access / NULL, / no security attrs */ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );

if (hCom == INVALID_HANDLE_VALUE) {
// Обработка ошибок }

// Установка масок событий Com порта. fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR); if (!fSuccess) {
// Обработка ошибок }

// Создать объект события для использования в функции WaitCommEvent o.hEvent = CreateEvent(NULL, /* no security attributes / FALSE, / auto reset event / FALSE, / not signaled / NULL / no name */); assert(o.hEvent);

if (WaitCommEvent(hCom, &dwEvtMask, &o)) {
if (dwEvtMask & EV_DSR) { /* */ }

<span class="token keyword">if</span>  <span class="token punctuation">(</span>dwEvtMask <span class="token operator">&amp;</span> EV_CTS<span class="token punctuation">)</span>
<span class="token punctuation">{</span>  
	<span class="token comment">/* */</span>
<span class="token punctuation">}</span>

}

Следующий пример открывает handle для COM1 и заполняет DCB структуру информацией о текущей конфигурации.
Сама DCB структура затем модифицируется и используется для реконфигурации устройства

DCB 	dcb; 
HANDLE 	hCom; 
DWORD 	dwError; 
BOOL 	fSuccess;

hCom = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, /* comm devices must be opened w/exclusive-access / NULL, / no security attrs / OPEN_EXISTING, / comm devices must use OPEN_EXISTING / 0, / not overlapped I/O / NULL / hTemplate must be NULL for comm devices */ );

if (hCom == INVALID_HANDLE_VALUE) { dwError = GetLastError(); // Обработка_ ошибки _handle }

// Опускаем вызов SetupComm, используя размеры очередей по умолчанию. Получаем текущую конфигурацию fSuccess = GetCommState(hCom, &dcb); if (!fSuccess) {
// Обрабатываем ошибку }

// Fill in the DCB: baud=9600, 8 data bits, no parity, 1 stop bit. dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT;

fSuccess = SetCommState(hCom, &dcb); if (!fSuccess) {
// Обрабатываем ошибку }

Алгоритм программирования СОМ-порта с помощью API-функций:

  1. Для работы с СОМ-портом необходимо открыть порт с помощью API функции CreateFile:
  2. После открытия СОМ порта можно передавать и принимать данные через этот СОМ-порт. Для передачи данных используется API функция WriteFile. Для приёма данных используется API функция ReadFile.
  3. После окончания работы с портом его нужно закрыть. Закрытие порта осуществляется API функцией CloseHandle.
  4. Настройка режима работы СОМ-порта осуществляется с помощью структур данных, которые представляют из себя набор переменных разного типа. Структуру загружаются и читаются с помощью API функций.

Структура DCB определяет основные настройки СОМ порта.
COMMPROP структура сообщает информацию о свойствах коммуникационного устройства.

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

Описание функций класса SerialGate:
SerialGate – библиотека для работы с COM портами на языке С++. Упрощает программирование последовательных портов за счет использования классов.
Имеет возможность определять все установленные в системе COM порты.
Корректно работает как с реальными так и виртуальными COM портами.
В основе библиотеки лежит класс SerialGate. Используя его методы можно выполнять наиболее часто востребованные действия на COM портом: прием-передача данных, управление линиями взаимодействия, определение доступных портов в сиcтеме и т.д.

Функция открывает доступ к к COM-порту с номером port на скорости baud байт/c. Если указанный порт существует и не занят другим приложением в данный момент, функция вернет true, иначе false. Если, к примеру, параметр port был указан как 3, то функция попытается открыть доступ к COM-порту с именем COM3. Функция корректно работает и с виртуальными портами.

bool Open(int port, int baud);

Пример использования:

SerialGate sg;

bool b = sg.Open(1, 19200); if(b == true) { // port is open sucsesfully } else { // port open error }


Функция записывает в ранее открытый порт szBuff байт данных из буфера buff. Возвращает число успешно записанных байт данных в порт.

int Send(char* buff, int szBuff);

Пример использования:

char buff[256];
for(int i=0; i < sizeof(buff); i++)
{
	buff[i] = i;
}
int SendCounter = sg.Send(buff, sizeof(buff));
if(SendCounter != sizeof(buff))
{
	//не все данные были записаны в порт
}

Читает из ранее открытого порта szBuff байт данных и помещает их в буфер buff. Возвращает число реально прочитанных байт данных.

int Recv(char* buff, int szBuff);

Пример использования:

char buff[256];
int RcvCounter = sg.Recv(buff, sizeof(buff));

if(RcvCounter != sizeof(buff)) { // прочли меньше, чем заказывали }


Функция устанавливает в логическую единицу или ноль одну из выходных сигнальных линий, а именно DTR или RTS. Имя линии задается через перечислитель OUT_LINES_NAME. Вторым параметром передается состояние (true – 1, false – 0) в которое необходимо перевести линию.

void SetLine(OUT_LINES_NAME ln, bool state);

Пример использования:

sg.SetLine(sg.RTS, true); // установит на линии RTS лог. 1

Функция возвращает состояние одной из входных сигнальных линий (CTS, DSR, RING или RLSD). Имя линии задается через перечислитель IN_LINES_NAME.

bool GetLine(IN_LINES_NAME ln);

Пример использования:

bool b = sg.GetLine(sg.DSR); // читаем состояние линии DSR

Функция заполняет переданную ей структуру PortInfo информацией о установленных в системе COM портах.

void GetPortsInfo(PortInfo* pi);

Очищает входной и выходной буфер данных COM-порта.

void Clean();

Пример использования:

sg.Clean();

Закрывает ранее установленное соединение с COM-портом.

void Close();

Пример использования:

sg.Close();

2. Подготовка рабочего окружения

В данном курсе используется достаточно современный стандарт языка программирования C++ (C++ 2014).
В качестве компилятора рекомендуется использовать gcc версии 5 и старше, clang версии >= 7.3. Для работы в среде Windows потребуется развернуть MSYS2.

Проект MSYS2 предоставляет для Windows пакетный менеджер Pacman, портированный из Arch Linux. С помощью пакетного менеджера можно устанавливать необходимые инструменты разработки, библиотеки, например, GTK+ или Qt5.

Установщик MSYS2 скачивается с сайта http://www.msys2.org/. MSYS2 не может быть установлен на диск с разметкой FAT** и на ОС Windows XP.

2.1 Установка и настройка MSYS2

  1. Скачиваем и запускаем установщик: для 64-битной системы x86_64, для 32-битной – i686.
  2. Указываем директорию установки. Путь должен быть коротким, содержать только латиницу с цифрами (ASCII). В пути не должно быть знаков ударений, пробелов и символических ссылок. Лучше оставить путь по-умолчанию или устанавливать в корень логического диска.
  3. Для работы через proxy в сети университета, необходимо отредактировать файл /home/st/.bash_profile (st – название домашней директории пользователя) в любом текстовом редакторе, добавив туда:
export http_proxy=http://proxy.ugatu.ac.ru:8008
export https_proxy=$http_proxy
export ftp_proxy=$http_proxy
export rsync_proxy=$http_proxy
export no_proxy="localhost, 127.0.0.1, localadress, .localdomain.com"
  1. После установки необходимо обновить основные системные пакеты MSYS2 и базу данных с информацией об доступных пакетах.
pacman -Syu

И соглашаемся на обновление, вводом y:
2019-11-14_23-11-23
Для завершения установки возможно потребуется вручную закрыть окно с терминалом MSYS2. И заново его запустить через ярлык в меню «Пуск»:
2020-09-24_18-38-15

2019-11-14_23-16-16
После перезапуска терминала MSYS2 , выполняем команду для обновления остальных пакетов:

pacman -Su

2.2 Установка набора компиляторов GCC для C/C++

У пакетного менеджера pacman имеется группа mingw-w64-x86_64-toolchain, в которой собраны компиляторы GCC, стандартные библиотеки и инструменты разработки:

2019-11-14_23-22-07
Для многопоточного программирования на C/C++ достаточно пакета mingw-w64-x86_64-gcc и mingw-w64-x86_64-winpthreads-git, с которыми по зависимостям установятся еще несколько необходимых пакетов:

pacman -S mingw-w64-x86_64-gcc
pacman -S mingw-w64-x86_64-winpthreads-git

Для работы с библиотекой libmodbus понадобиться установить пакет mingw-w64-x86_64-libmodbus-git, для этого можно выполнить команду:

pacman -S mingw-w64-x86_64-libmodbus-git

Если MSYS2 не обновляется и выдает ошибку “неизвестный ключ”

Если вы не обновляли MSYS2 на своём ПК с начала июня 2020 года, при попытке обновления вы получите такую ошибку:

ошибка: mingw64: неизвестный ключ "4A6129F4E4B84AE46ED7F635628F528CF3053E04"

В чем же дело? MSYS2 - https://www.msys2.org/news/#2020-06-29-new-packagers

Оказалось, что Alexey Pavlov (Alexpux) <alexpux@gmail.com>; больше не занимается подписанием пакетов, соответственно его ключ был отозван, так что нам нужно обновить PGP ключи MSYS2.

Для этого введем следующие команды

$ curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz 
$ curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig 
$ pacman -U --config <(echo) msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz 

И, на всякий случай, очистим хранилище PGP ключей и обновим его содержимое:

$ rm -r /etc/pacman.d/gnupg/ 
$ pacman-key --init 
$ pacman-key --populate msys2

После этого запускаем обновление:

pacman -Syu

Обязательно, перезагрузите ПК, иначе консоль не запуститься после обновления pacman.

2.3 Проверка окружения

После установки необходимо проверить текущее местоположение в файловой система – домашний каталог активного пользователя. С помощью команды pwd:
2019-11-14_23-32-06

2.4 Компиляция и флаги

Для компиляции исходных кодов на языке с++ воспользуемся оболочкой g++ toolchain (набора утилит) gcc. Инструкция по работе:

g++ -Wall -pedantic server.cpp SerialGate.cpp -o ./build/server.exe
Ключи Значение
-Wall -pedantic выводить информацию о всех предупреждения компилятора
server.cpp SerialGate.cpp Файлы, которые необходимо скомпилировать
-o ./build/server.exe Имя и название файла, куда необходимо собрать исполняемый файл

3. Задание на лабораторную работу

  1. С помощью программы Virtual Serial Ports Emulator необходимо создать пару виртуальных портов с именами COM5 и COM6
    изображение
    Главное окно приложения

изображение
Режим с выводом дополнительной информации

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

  1. Протестировать приложение обмена текстовыми данными server и client
  • Запустить приложение Lab_1\comPortsConnect\build\server.exe. Для этого рекомендуется открыть два окна терминала (последовательность действий 1-2-3 для открытия одного окна)
    изображение
    изображение
    Если увидели, что приложение запустилось, но ничего не выводит на экран терминала, то действуем, добавляя код в тело main (в заготовках уже добавил), и перекомпилируя:
setvbuf(stdout, NULL, _IONBF, 0);
  • Запустить приложение Lab_1\comPortsConnect\build\client.exe

изображение

  • Осуществить обмен информацией между двумя приложениями через связанную пару виртуальных портов. Передать ФИО и группу, например: Hi, Ivanov from IVT-431. Скриншот с такими данными является строго обязательным для отчета.
  • Проанализировать исходный код каждого приложения и вынести в отчет исходный код, отвечающий за процедуру подключения к портам и обмена информацией.
  1. Необходимо ознакомиться с основными программно - доступными элементами адаптера.
  • Необходимо с использованием примеров написать клиентскую и серверную программы на языке С/С++ для реализации обмена через последовательный порт по полудуплексному вводу и выводу (с использованием WIN API). Код программ приведен в файлах директорий client и server:
  • Компиляция осуществляется средствами mingw. Необходимо запустить терминал и перейти в директорию с исходным кодом проекта. Далее запустить из консоли командный файл compile.cmd.
    изображение
  • Модернизировать клиент и сервер так, чтобы можно было обмениваться сообщениями в двухстороннем режиме (“полудуплексный консольный чат”).
  • Отладить и протестировать программы.
  1. Проброс COM портов через TCP/IP с помощью Virtual Serial Ports Emulator (Windows) и взаимодействие с удаленным сервером
  • Выполнить проброс COM-порта
    изображение
    Создаем коннектор к виртуальному COM-порту, с которым будет работать клиентское приложение

изображение
Выбираем номер порта

изображение
Устанавливаем тип TCPClient

изображение
Указываем IP-адрес удаленного сервера (обязательно тот, который указан на скриншоте) и номер порта (аналогично). Выбираем COM-порт, который будет проброшен

изображение
Connector и TcpClient сконфигурированы

  • установить параметры подключения к удаленному COM-порту сервера
int portRate = 9600;       // Скорость обмена
  • передать строку со своей фамилией и группой в качестве посылки на сервер. Например:
petrov_IVT_574
  • сохранить ответ сервера в отчет и использовать его как ключ при отправке лабораторной работы на проверку

4. Требования к содержанию и оформлению отчета

Отчет оформляется на листах формата А4 а соответствии с требованиями стандартов ЕСКД. Отчёт о лабораторной работе должен содержать:

  1. название, цель и задачи работы;
  2. скриншоты процедуры создания пары виртуальных портов, краткая характеристика программы создания;
  3. скриншоты тестирования обмена данными через пару виртуальных портов с помощью программ client и server, ключевые участки исходного кода приложений, описание используемых библиотек;
  4. исходный код приложений, осуществляющих обмен данными по COM-порту, используя API Windows NT;
  5. исходный код и скриншоты приложения для взаимодействия с удаленным сервером с проброшенным COM-портом;
  6. выводы о проделанной работе в целом.

Контрольные вопросы

  1. В чем заключается старт-стопный способ синхронизации?
  2. Какое напряжение соответствует логической единице в интерфейсе RS232?
  3. В чем заключаются особенности использования последовательного интерфейса RS-232С?
  4. Опишите организацию сопряжения двух устройств через RS-232С.
  5. Охарактеризуйте внутреннее аппаратное устройство, разъем и кабель порта RS-232С?
  6. Объясните порядок обмена по интерфейсу RS-232С.
  7. Приведите типы последовательных интерфейсов и опишите их особенности.
  8. Опишите особенности программирования интерфейса RS-232С.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment