COM-порт и интерфейс USART — связь с ПК

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

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

Основной темой будет создание приложения для ПК, которое может взаимодействовать с микроконтроллером через последовательный порт. Приложение предназначено для систем, использующих USART. Такое решение позволяет передавать данные не только через RS232, но также через USB и конвертер FT232, bluetooth, IrDA и другие интерфейсы с использованием виртуального COM-порта.

Интерфейс USART

USART — универсальный синхронный и асинхронный приемник и передатчик

Как уже упоминалось, USART — это интерфейс связи, доступный в большинстве популярных микроконтроллеров. В некоторых из них можно даже найти несколько USART,  работающих независимо.

Для работы мы будем использовать ATmega16, но программы должны быть легко перенесены на другие связанные процессоры. Сигналы TxD (передача данных) и RxD (прием данных) используются для передачи данных через USART.

Характерной особенностью USART в асинхронном режиме является отсутствие тактового сигнала.

Скорость передачи данных определена для соединения заранее. Это делается путем ввода соответствующего значения в UBRR (регистр скорости передачи USART), рассчитанного на основе тактовой частоты микроконтроллера.

Кстати, существуют стандартные скорости передачи: 2400, 4800, 9600 и т.д. Для обеспечения передачи с ошибкой 0,00%, тактовая частота микроконтроллера должна быть заранее известна и стабильна, но также должна быть кратна 1,8432 МГц. Выбор другой частоты снизит точность.

Но по-прежнему допустимы ошибки менее 2%.

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

Фрейм данных в передаче UART выглядит следующим образом:

Фрейм данных UART

Он включает в себя стартовый бит для оповещения о начале следующего кадра (от 5 до 9 битов данных) и бит четности для проверки ошибок при приеме. Он может работать в трех режимах — без управления (no), сумма всегда четная (even), сумма всегда нечетная (odd).

Каждый кадр закрывается одним- двумя стоповыми битами . Наиболее распространенная конфигурация — 8 бит данных, без контроля четности и 1 стоповый бит.

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

Ниже представлена ​​схема подключения UC к RS:

схема подключения UC

Другие, частые конфигурации — это USART, работающий с FT232RL и подключенный к USB-порту, модуль Bluetooth, например, BTM-222 (помните о напряжении питания 3,3 В), или соединение двух микроконтроллеров друг с другом. Но мы не будем о них писать. Схемы подключения без проблем можно найти в Интернете. Перейдем к программе со стороны ПК.

Среда разработки

Главный критерий выбора среды разработки — наличие выделенных библиотек для поддержки последовательных портов.

Альтернатива, которая заключается в прямом использовании Win32 API, трудна, неинтуитивна и иногда плохо читается.

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

Мы выбрали платформу .NET и Visual C# 2010 для написания своей программы, но ничто не мешает вам написать такую ​​программу, например, на Java. Процесс разработки приложений и даже необходимые функции в Java очень похожи, поэтому проблем с переводом на другой язык возникнуть не должно .

Если кто-то уже имел дело с C, C++ или Java, переход на C# не должен стать большой проблемой. Пользователей, предпочитающих Bascom, Pascal и другие подобные языки, может заинтересовать Visual Basic. Однако мы рекомендуем им C#, потому что он представляет собой гораздо более популярное семейство языков, и в Интернете легче найти по нему помощь.

Visual C# Express 2010 можно бесплатно загрузить с веб-сайта Microsoft , школьники и студенты могут бесплатно загрузить Visual Studio 2010 с MSDNAA. Для остальных лицензия на Visual Studio, к сожалению, платная. Мы же используем экспресс-версию.

После успешной установки откройте Visual C# 2010 Express и выберите:

File->New Project

Появится следующее окно (в полной версии Visual Studio 2010 есть выбор из большего количества типов проектов).

Visual C# 2010 Express

Мы выбираем Windows Forms Aplication Visual C# , а внизу выбираем имя для нашего проекта и нажимаем ОК. Мы должны увидеть представление Designer:

Forms Aplication Visual C#

В нем мы увидим, как выглядит наше приложение. Если теперь мы нажмем зеленую кнопку «Play» на верхней панели, или «Debug->Start», мы увидим, что наша программа в настоящее время является лишь пустым окном, и ее единственная функция — 3 кнопки в правом верхнем углу.

Рекомендуется сразу расположить панели параметров так, чтобы работать с программой было удобнее. Сначала нажмите на молоток, отмеченный кружком. Он активирует панель с элементами, которые можно добавить в нашу форму.

Чтобы изменить положение панели, нажмите на нее левой кнопкой мыши и переместите. Мы расположили ее справа, а затем щелкнули на булавке вверху, которая называется «Auto Hide» . Теперь, когда мы наведим указатель мыши на панель справа, открывается панель инструментов.

После щелчка правой кнопкой мыши в окне Form1 и выбора «Properties» появятся параметры объекта. Этой вкладкой мы будем пользоваться часто. Вот почему мы переместили его вправо без автоматического скрытия. А проект перенесли в левую часть экрана. В конечном итоге все выглядит так:

Читать также:  Что такое ИБП?

Вот так выглядит

Введение в C#

Теперь несколько слов о языке C#. Это объектно-ориентированный язык, то есть он состоит из элементов (объектов), принадлежащих классам. Каждый класс имеет свои собственные параметры — переменные и методы — функции, которые действуют на эти параметры, возвращают их значения, конвертируют в другие типы и т.д.

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

Специальные методы — это конструктор (функция с тем же именем, что и класс, классы могут иметь более одного конструктора, каждый имеет то же имя, но разные аргументы) и деструктор. Они отвечают за инициализацию и завершение работы объекта, принадлежащего данному классу.

Кроме того, классы могут наследовать параметры и методы, параметр одного класса может быть другим и т.д. Типы переменных, такие как int и double, также являются классами в C#. Таким образом, можно использовать методы, которыми они оснащены. Например, ToString () выводит, например, число int в виде строки. Очевидно, что это не меняет текущее использование этих типов переменных. Следовательно, целые числа по-прежнему можно добавлять, вычитать и т.д.

В любом случае такая теория чистого языка не имеет большого смысла, поэтому мы сразу перейдем к созданию нашего приложения. Если кого-то больше интересуют основы C# и программирования на платформе .NET, мы рекомендуем заглянуть в обширную библиотеку Microsoft, а также воспользоваться поиском в Интернете, где есть огромное количество руководств и курсов для начинающих.

Создание приложения

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

На первых этапах имеет смысл сделать простой терминал с двумя текстовыми полями. Один будет отображать отправленные сообщения, а другой — полученные сообщения, или одно текстовое поле для всех сообщений. Позже можно будет расширить функциональные возможности для передачи данных в соответствии с некоторым более продвинутым протоколом, например, для непрерывного получения данных от датчиков робота, чтения содержимого EEPROM или для управления сервоприводами.

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

Давайте увеличим наше окно. В опциях вы также можете изменить имя, текст и размер шрифта для облегчения работы в дальнейшем и улучшения эстетики. Затем, найдя его на панели инструментов в группе Containers TabControl, давайте добавим его в наше окно. В опциях Tab найдите параметр Dock и установите для него значение Fill. После этого он заполнит все окно. Затем найдите параметр «Tab Pages» и нажмите кнопку «…». Там мы добавляем третью вкладку и меняем имя и отображаем текст, как показано на картинке ниже:

Добавляем третью вкладку

Название и текст второй таблицы мы пока не меняли. Сначала мы сделаем терминал, а вкладку зарезервируем для более изощренного способа связи. Вы же можете использовать только вкладки с параметрами и терминалом, т.к. управлять вкладками очень просто и требует только использования кнопок Add и Remove. Теперь переходим на вкладку параметров и добавляем такие элементы, как label, combobox и button.

Не забывайте изменить поля «Text» и «Name» в параметрах. Мы меняем имена на такие, чтобы было понятно, для чего предназначен каждый элемент.

Для ComboBox, связанных со скоростью передачи и количеством битов данных, элементы этого набора должны быть определены вручную. Найдите опцию «Items» и нажмите на точки рядом со словом «Collection». Здесь мы вводим значения от 5 до 9 для битов данных, каждое из которых разделяется вводом. Во втором вводим все стандартные скорости передачи, то есть:

После создания вкладки опций, аналогичной изображенной на картинке, мы можем перейти к проектированию терминала. Наш выглядит так:

Создание вкладки опций

Он состоит из двух кнопок. Красный квадрат будет кнопкой начала / завершения и одновременно покажет текущий статус.

В большом текстовом поле будут показаны отправленные / полученные данные в шестнадцатеричном коде. В маленьком числовом поле вы сможете выбрать значение нового байта для отправки в шестнадцатеричном коде.

Нажатие кнопки «Отправить» отправит текущее значение через COM-порт, а «Очистить» очистит журнал действий. Следует помнить о некоторых настройках параметров. В PictureBox мы устанавливаем BackColorRed , в RichTextBox мы устанавливаем ReadOnlyTrue, чтобы случайно не изменить содержимое логотипа.

В NumericUpDown мы меняем параметр Hexadecimal на true, чтобы числа отображались в шестнадцатеричном формате, а минимальные и максимальные значения на 0 и 255 соответственно, чтобы они помещались в один байт.

Таким образом, нам удалось завершить этап «проектирования» нашего приложения. До сих пор все было просто и не имело ничего общего с настоящим программированием. Наше приложение уже выглядит неплохо, и после компиляции оно даже получило некоторые функции, такие как выбор байтового значения или возможность выбора некоторых параметров из раскрывающегося списка.

Читать также:  Инклинометр: функции, применение

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

Программирование

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

Затем у нас есть название нашего основного класса и его конструктора, который содержит функцию InitializeComponent. Он создает окно, которое мы видим после компиляции программы.

Вопрос в том, что это за волшебная функция, которая не принимает никаких параметров и точно знает, какое окно она должна создать для нас. Ответ на этот вопрос скрыт под ключевым словом partial и в другом файле нашего проекта — Form1.Designer.cs. Если мы откроем его, мы также увидим определение нашего класса со словом partial в пространстве (местоположении) имен TutorialCOM. Это, конечно, вторая часть нашего названия основного класса. Здесь находится функция InitializeComponent, которая автоматически обновляется, когда мы что-то добавляем в конструктор.

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

Те, которые в настоящее время сгенерированы в нашем файле, относятся к классам, используемым в дизайнере. Что означает использование using? Проще всего это проиллюстрировать на примере.

Для нашей программы нам понадобится пространство имен System.IO.Ports, где есть класс SerialPort, который обрабатывает последовательный порт. Нам нужно добавить элемент указанного выше класса в нашу программу, чтобы установить соединение. Внутри класса мы можем определить переменную:

System.IO.Ports.SerialPort port;

Если мы хотим продолжим ссылаться на элементы этого пространства имен, то каждый раз заново должны писать System.IO.Ports. Кстати, после ввода точки мы видим, что Visual Studio отображает раскрывающийся список с доступными элементами для выбора, что значительно облегчает написание. Тем не менее, размещение этого пути каждый раз значительно расширяет написание. Вот почему в начальной части файла мы можем добавить:

using System.IO.Ports;

Теперь наше определение переменной может быть сокращено до:

SerialPort port;

С этого момента нам не нужно каждый раз писать полный путь. Ключевое слово using позволяет компилятору понимать, где искать классы, которые мы используем. У нас добавлена ​​переменная типа SerialPort. Теперь нам нужно инициализировать его в конструкторе следующим кодом:

Мы изменим параметры подключения в соответствующей вкладке. И нам нужно убедиться, что там отображаются правильные значения, как текущие, так и доступные в раскрывающихся списках. Для этого нам нужно создать обработчик событий, который будет запускаться всякий раз, когда мы будем переходить на вкладку параметров. Для этого в конструктор добавляем событие:

и ниже функция, которая его поддерживает:

Options.Enter — это событие, назначенное объекту с именем options и запускаемое каждый раз, когда мы открываем эту вкладку. Каждый раз, когда происходит это событие, все обработчики событий (то есть функции) в нем выполняются.

Вы можете добавить обработчик события + = или вычесть — = для события. Однако в функции обработки событий, помимо простого присвоения Strings текстам полей, у нас есть интересная структура foreach. Она используется для работы со всеми возможными элементами, которые могут быть найдены в данном наборе, особенно, когда мы еще не знаем, сколько их будет.

В нашем случае, мы добавляем в список все элементы String, найденные в наборах имен портов, параметров четности и параметров остановки. Таким образом, если мы что-то подключим к пяти COM-портам, список будет включать 5 пунктов, а если мы ничего не подключим, список будет пустым. Это чрезвычайно полезная команда.

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

Если мы включим программу, скомпилированную на этом этапе, то увидим, что, если у нас открыта вкладка параметров, и что-то вставлено в порт, изменение не будет замечено, пока мы не выйдем и не войдем повторно в программу.

Вот почему мы добавили кнопку «Обновить» , с помощью которой мы сможем немедленно обновить эти параметры. Если мы откроем конструктор и дважды щелкнем на кнопку «Обновить», весь код для обработки события после нажатия кнопки будет сгенерирован автоматически. Однако для нас это событие будет выглядеть точно так же, как и в случае активации опции. Таким образом, вы можете удалить сгенерированную функцию-обработчик и в Form1.Designer.cs найти строку:

и замените его на:

Теперь давайте создадим событие нажатия кнопки по умолчанию, чтобы оно автоматически изменяло значения на все, что мы захотим:

С другой стороны, для кнопки «Отмена» пусть принимаются те же значения, что и при активации вкладки:

У нас есть готовая вкладка с параметрами. Мы можем выбрать свойства подключения из списка, а нажатие кнопки запускает соответствующее действие. Теперь по порядку на вкладке «Терминал». Красный квадрат используется для установления соединения. Для этого создайте событие и поместите туда следующий код:

Читать также:  Что такое госсистема оценки качества образования?

На первый взгляд это может показаться трудной работой, но мы сейчас разберем все досконально.

Если порт открыт, мы завершаем соединение и устанавливаем окно изображения и метку как в начале. Однако, если порт закрыт, и мы хотим его открыть, мы должны учитывать возможность ошибок. Вот почему мы используем структуру try-catch.

Сначала выполняются команды в скобках try. Если программа обнаруживает ошибку, она переходит в раздел перехвата и отображает сообщение об ошибке на экране. Если бы этот фрагмент отсутствовал, программа бы не работала. Если она без проблем выполняет все содержимое скобки try, то пропускает часть catch и продолжает работу.

Содержимое скобки try сначала состоит из перезаписи значения порта из опций, открытия порта и установки цвета квадрата на зеленый, а текста — на информацию об активном соединении. Функция также отправляет на терминал соответствующее сообщение о состоянии оранжевого цвета. Для этого предназначена функция AddColor, к которой мы еще вернемся.

Отправка данных

Когда мы нажимаем кнопку «Отправить», текущее значение поля NumericUpDown должно быть отправлено через COM-порт. Для этого создаем еще одно событие двойным щелчком в дизайнере. Вот следующий код:

Здесь стоит отметить несколько моментов. Во-первых, функция ToString с аргументом «X» преобразует числа типа int в шестнадцатеричный код. Условие if-else предотвращает попытку отправки сообщения при неактивном соединении. Теперь давайте сосредоточимся на функции AddColor. Изменение цвета шрифта необходимо, поскольку исходящие, входящие и статусные сообщения должны отображаться в одном текстовом поле. Однако, если мы попробуем сделать это обычным способом и добавим текст xxx.Text + = «string»; предыдущий текст также изменит цвет, полностью испортив эффект. Следовательно, вам нужно обойти проблему с помощью дополнительной функции, которая использует команду AppendText :

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

Получение сообщений

В конце концов, у нас осталась самая сложная задача. Нам нужно добавить событие, которое отображает входящее сообщение в TextBox. Начнем с добавления класса события в конструктор из входящих данных в COM-порт:

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

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

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

Делегат скрывает в себе призыв к каким-то функциям. Чтобы использовать его, необходимо создать объявление делегата и объект типа объявленного делегата. Мы помещаем код в тело класса, рядом с объявлением переменной port. В нашем случае это будет выглядеть следующим образом:

Таким образом, мы объявили делегата, который возвращает значение void и не требует никаких аргументов, и объект типа Delegate1 с именем my_del1. Затем в конструкторе функции следует произвести инициализацию:

EnterOdebrane — это функция, которую мы собираемся создать, и перед ней будет задача добавления полученного текста в текстовое поле. Вы можете увидеть большое сходство с событиями при инициализации делегата. Если бы мы назвали нашего делегата xxxEventHandler вместо Delegate1, то не было бы никакой разницы.

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

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

Вызов содержит ранее упомянутое слово Invoke. Он отвечает за безопасный вызов функции делегата, чтобы не мешать работе других потоков. После добавления этой строки у нас есть полностью функциональный терминал. Теперь просто выберите debug-> build solution в верхнем меню, найдите папку проекта на диске, войдите в каталог bin / Release и найдите там exe-файл с нашей программой.

Правильность работы программы проще всего проверить, соединив выводы Tx и Rx устройства, подключенного к порту, между собой.

Мы понимаем, что некоторое из обсуждаемого в статье покажется вам непостижимым, поэтому мы предлагаем вам скачать весь исходный код программы здесь. Кстати, рекомендуем самостоятельно изучать материалы в Интернете, если вы хотите в будущем использовать C# для других своих проектов.

Вывод

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

С Уважением, МониторБанк

Добавить комментарий