[Из песочницы] Организовываем взаимодействие между ПК и ЦАП/АЦП при помощи ПЛИС

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

Однако использование или отладка ЦАП/АЦП могут быть затруднены поставленными производителем аппаратуры ограничениями, например, на используемое ПО или на способы управления устройством. Это наводит на мысль о проектировании собственной модели взаимодействия.

В этой статье мы рассмотрим возможность организации взаимодействия между ПК и ЦАП/АЦП при помощи ПЛИС.

fknrk3dh-lzetqng6rmbke0fgls.jpeg

Предисловие


Основной целью данной статьи является описание решения конкретной задачи, а так же знакомство с интерфейсами взаимодействия интегральных микросхем (ИМС). Внимание основному предназначению ЦАП/АЦП здесь не отводится.

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

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

Всем удачи и новых свершений!

Небольшой ликбез на тему интерфейсов


Дисклеймер


Если вы знакомы с устройством основных интерфейсов, использующихся для коммуникации ИМС, вы можете спокойно пропустить этот пункт.

ликбез

Общая информация


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

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

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

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

По способу соединения


С точки зрения способа соединения устройств (то бишь топологии сети), описанные в этой статье интерфейсы могут быть организованы в соединения:
  • «точка-точка»;
  • «шина»;
  • «кольцо» («цепочка»).

Соединение «точка-точка» — простейший вид соединения, при котором два устройства соединяются между собой напрямую. Достоинством такого вида соединения является простота и дешевизна, недостатком — невозможность соединения таким способом более двух устройств.

xw-ulkit9dhae9jx_rtbyt7b1jk.jpeg

Топология «шина» (или, как ее еще часто называют «общая шина» или «магистраль») предполагает использование как минимум одного общего канала, к которому подсоединены все приемопередающие устройства. Общий канал используется всеми устройствами по очереди. Все сообщения, посылаемые отдельными устройствами, принимаются и прослушиваются всеми остальными устройствами, подключенными к сети. Из этого потока каждый абонент сети отбирает адресованные только ему сообщения.

pkrdfsvvnedy66ogrl-y_769kgw.jpeg

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

2n9ar1pdkgj7xyk_r4vmct6jplo.jpeg

По способу передачи информации


По способу передачи информации интерфейсы различают:
  • параллельные;
  • последовательные;
  • параллельно-последовательные.

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

zhyyibvqbt5idtn8hswddqxjpdc.jpeg

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

vxbhukhy5lhrm3xnqtj7m7zggri.jpeg

Смешанный (параллельно-последовательный/байтовый) способ широко используется в системах с различной разрядностью представления данных у разных устройств

По режиму передачи


По режиму передачи информации различают интерфейсы:
  • с двусторонней одновременной передачей (дуплексные);
  • с двусторонней поочередной передачей (полудуплексные);
  • с односторонней передачей (симплексные).

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

lphwljkkotct0naxv7wd0e04kfs.jpeg

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

xw-ulkit9dhae9jx_rtbyt7b1jk.jpeg

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

eg7-0imdoh0mgjb712a4gfdfixs.jpeg

По принципу обмена информацией


По принципу обмена информацией интерфейсы подразделяют на:
  • синхронные;
  • асинхронные.

При асинхронной передаче каждый символ передается отдельной посылкой. Стартовые биты предупреждают приемник о начале передачи. Затем передаются информационные биты. Для определения достоверности передачи используется бит четности (бит четности — один из механизмов проверки принятых данных, по простому — если он равен единице, то количество единиц в посылке нечетно, и нулю в противном случае). Последний бит «стоп бит» сигнализирует об окончании передачи (может быть не использован в зависимости от интерфейса и его конфигурации). В некоторых системах используется механизм подтверждения принятия сообщений.

euexsjckmvbxcwj2k-xthfbexla.jpeg

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

abqn2qpz4-wrwrn4pr_-amtwye8.jpeg

О каких интерфейсах пойдет речь?


В настоящий момент самыми распространенными интерфейсами для взаимодействия между интегральными микросхемами (ИМС) являются синхронные SPI и I2C и асинхронный UART.

I2C


I2C — последовательная асимметричная шина для связи между интегральными схемами внутри электронных устройств. Шина I2C применяется для соединения низкоскоростных периферийных компонентов с процессорами и микроконтроллерами. I2C реализовывает передачу данных в полудуплексном режиме в формате «Ведущий-ведомый», в котором поддерживается несколько ведущих устройств.

Концепция стандарта состоит в том, что каждое подключенное к шине устройство определяется уникальным адресом, используемым при обращении к этому устройству. Такая концепция значительно упрощает идентификацию устройства, что позволяет интерфейсу иметь всего два канала. Классическая адресация включает 7-битное адресное пространство с 16 зарезервированными адресами. Это означает, что разработчикам доступно до 112 свободных адресов для подключения периферии к одной шине. Все операции по шине I2C осуществляются при помощи канала последовательных данных (SDA) и канала синхронизации (SCL).

ln1ve-yx8hug2egvkq_5e3xvlem.jpeg
Диаграмма передачи данных по интерфейсу I2C

SPI


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

Шина SPI организована по принципу «ведущий-подчиненный». Ведущий шины передает данные по линии MOSI синхронно со сгенерированным им же сигналом SCLK, а подчиненный захватывает переданные биты данных по определенным фронтам принятого сигнала синхронизации. Одновременно с этим подчиненный отправляет свою посылку данных по линии MISO, тем самым организую дуплексную передачу данных. Так же подчиненное устройство ждет сигнала разрешения на передачу данных по линии SS.

zs6mrgcpz1qrdmmmfxpocz9ptvo.jpeg
Диаграмма передачи данных по интерфейсу SPI

UART


UART — универсальный асинхронный приемопередатчик — интерфейс для связи цифровых устройств, предназначенный для передачи данных в последовательной форме.
Основными каналами данного стандарта являются RxD (принимающий канал) и TxD (передающий канал).

Передача данных в UART осуществляется по одному биту по каналу TxD в равные промежутки времени. Этот временной промежуток определяется заданной скоростью и для конкретного соединения указывается в бодах. Так как UART является асинхронным интерфейсом, то помимо информационных бит, UART автоматически вставляет в поток синхронизирующие метки, так называемые стартовый и стоповый биты. При приеме данных эти биты не учитываются, так как они используются только для маркировки старта и окончания передачи информации. Обычно стартовый и стоповый биты обрамляют 8 бит данных (максимальный размер посылки), при этом младший информационный бит передается первым, однако эта конфигурация является настраиваемой.
Изначально UART предназначался для связи двух устройств по принципу «точка-точка». Впоследствии были созданы физические уровни, которые позволяют связывать более двух UART по принципу полудуплекса. Существуют реализации типа «общая шина» и «кольцо».

5w5ui_cf8lwfgieqdopo7vwcvd0.jpeg
Диаграмма передачи данных по интерфейсу UART


Что это за ЦАП?


pt8zd0jzabm-e-kgbtz06ol_bik.jpeg
Вроде не этот

Для начала определимся с используемым ЦАП/АЦП:

Отладочная плата Analog Devices AD9993-EBZ представляет собой устройство, объединяющее четыре 14-битных АЦП и два 14-битных ЦАП.

Управление режимами работы этой ИМС осуществляется за счет изменения состояния ее внутренних регистров. В AD9993 используются 66 регистров с размером хранимого (обрабатываемого) слова в 8 бит, адреса которых описываются трехразрядными шестнадцатеричными значениями (12 бит данных). Поддерживаются два типа команд — команда чтения и команда записи. Другими словами, для того, чтобы корректировать работу ИМС, необходимо передать команду записи некоторого допустимого 8-битного значения по определенному адресу регистра. Доступ к регистрам данной ИМС осуществляется через последовательный периферийный интерфейс (SPI), подключение к которому на отладочной плате реализовано через 40-контактный разъем FIFO_0 или через дополнительно устанавливаемую контактную группу.

2wy6otsylp7wpg7qmzf7gtdlinm.jpeg
Вроде этот (FIFO_0 — верхний серый разъем)

Основные требования по предельным параметрам интерфейса SPI:

  • Максимальная частота передачи данных — 25 МГц;
  • Продолжительность верхнего и нижнего уровня на линии генерации синхросигналов — 10 нс;
  • Время установки уровня на линиях передачи данных и на линии разрешения — 2 нс;
  • Время удержания уровня на линиях передачи данных и на линии разрешения — 2 нс;
  • Гарантированное время действительности одного бита данных на линии передачи ведомого устройства — 2нс.


Полное описание характеристик устройства можно найти в его документации.

Что говорит производитель?


Рекомендованная схема управления


Исходя из документации AD9993-EBZ, рекомендованным способом управления ИМС является использование отладочной платы Analog Devices HSC-ADC-EVALC, подключенной к ИМС по 40-контактному порту, и к ПК по интерфейсу USB.

В качестве ПО для управления используется SPIController.

8ngbe54iskwttkwpgyjftmhcg64.png
Рекомендованная схема

gusyyhepzen1v1lqigviggseuvm.jpeg
Analog Devices HSC-ADC-EVALC (базируется на ПЛИС Xilinx Virtex-4)

минусы:

  • Высокая стоимость. Отладочная плата HSC-ADC-EVALC на официальном сайте Analog Devices предлагается по цене $698.28.
  • Неудобный пользовательский интерфейс. ПО SPIController, помимо основной функции записи и чтения регистров, не предоставляет возможности сохранения или запланированного запуска команд.
  • Ограниченная совместимость. Для управления других ИМС с возможностью взаимодействия по интерфейсу SPI требуется найти и добавить отдельный файл конфигурации для ПО SPIController, разработанный специально для этой ИМС. Список доступных для управления ИМС ограничен продукцией Analog Devices, поддерживающий конфигурирование по интерфейсу SPI.


8oqp_vvrz82xywdttyadum15dzs.jpeg
Пользовательский интерфейс SPIController

плюсы:

  • Официальная поддержка производителя.


Немного об HSC-ADC-EVALC


Хочется отметить, что использование платы Analog Devices HSC-ADC-EVALC в качестве устройства управления ЦАП/АЦП не является его главным предназначением.

HSC-ADC-EVALC главным образом используется как плата буферной памяти для АЦП, однако она также имеет функционал конфигурирования отладочных плат через интерфейс SPI, если подключенные платы это поддерживают.

Собственная модель взаимодействия


Причина использования промежуточного устройства


Очевидным является факт того, что взаимодействие ЦАП/АЦП и ПК невозможно организовать напрямую, так как SPI, с помощью которого предусмотрено программирование AD9993-EBZ, не является типовым интерфейсом современного ПК.

Для решения поставленной задачи необходимо было использовать устройство-посредник, который бы преобразовывал данные из интерфейса USB, посылаемые с компьютера, в формат интерфейса SPI, поддерживающийся ИМС.

В ходе прорабатывания вариантов выбор пал на использование отладочной платы Terasic DE10-Nano, работающей на базе ПЛИС Cyclone V.

8tgfvsxaauhajrghs5wpxw8ovz0.jpeg
Собственная модель взаимодействия

Почему ПЛИС — это круто?


Основные преимущества использования ПЛИС:

  • Удобство разработки. DE10-Nano позволяет гибко реализовать взаимодействие с подключенными устройствами, так как в отличие от обычных цифровых микросхем, логика работы ПЛИС не определяется при изготовлении, а задается посредством программирования. Для программирования используются программатор и IDE, позволяющие задать желаемую структуру цифрового устройства в виде принципиальной электрической схемы или программы на языке описания аппаратуры Verilog.
  • Низкая стоимость. Цена DE10-Nano выгодно отличается от HSC-ADC-EVALC ($110 против $698.28). Помимо этого DE10-Nano все еще остается универсальным устройством, способы применения которого ограничиваются только фантазией его владельца.
  • Перспективы. В дальнейшем проект для ПЛИС можно дополнить функционалом буферной памяти для АЦП, тем самым полностью заменяя предлагаемое производителем решение.
  • Коммьюнити. В интернете имеется огромное количество FPGA-ориентированных блогов и статей (FPGA — field-programmable gate array — интернациональное определение полупроводниковых устройств, программируемых на уровне принципиальной схемы), что является неоспоримым плюсом для неопытного инженера (коим автор статьи и является). Отдельного упоминания заслуживает форум rocketboards c кучей готовых проектов и гайдов к ним.


Процесс проектирования


Ничего не предвещало беды…


В ходе проектирования модели взаимодействия было принято решение реализовать интерфейс SPI на основе GPIO (пинов общего назначения), контактная база которого имеется на DE10-Nano. Реализация контроллера SPI на базе ПЛИС не должна была создавать особых проблем благодаря большому количеству материалов на схожую тему.

Однако затруднения вызвала реализация подключения ПЛИС к компьютеру по интерфейсу USB.

DE10-Nano имеет следующие порты USB:

  • USB mini-B под управлением чипа FT232R, реализующий подключение UART to USB.
  • USB mini-B под управлением чипа SMSC USB3300, реализующий физический уровень интерфейса USB, а так же использующийся для программирования ПЛИС.


Использование этих портов затруднено тем, что коммуникация DE10-Nano с этими портами происходит через так называемую HPS (hard processor system) — часть чипа Cylone V, которая содержит микропроцессорный модуль, процессор ARM Cortex, контроллеры флеш-памяти и многое другое. Основное отличие HPS от ПЛИС — HPS представляет собой блоки неизменной структуры, оптимизированные для определенной функции и не имеющие средств её программирования (поэтому и hard).

Части HPS и ПЛИС чипа Cyclone V имеют свои собственные контакты. Эти контакты не делятся свободно между HPS и ПЛИС. Контакты HPS конфигурируются программным обеспечением, выполняемым в HPS. Контакты ПЛИС программируются с помощью образа конфигурации ПЛИС через HPS или любой другой поддерживающийся внешний источник.

Для того, чтобы организовать взаимодействие программируемой логики чипа Cyclone V с этими портами, необходимо создать специальный загрузчик Linux, работающий на HPS, а также необходимо разработать программу, которая способна передавать сигналы с контроллеров доступных портов USB на свободные контакты ПЛИС.

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

Выход есть!


Плотно прошерстив просторы Интернета, решено было использовать внешний контроллер UART.

Внешний контроллер UART представляет собой небольшую плату на базе чипа FT232RL. На плате установлены разъемы miniUSB-B для связи с компьютером и 6-контактный разъем для связи с микроконтроллерами и устройствами.
Подключение контроллера происходит по интерфейсу USB к ПК, и через контактную базу GPIO к DE10-Nano.

fvidd1-wpdlt70msswye7_2xb9q.jpeg
Сам контроллер от компании Waveshare (использован в проекте)

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

Помимо линий питания и земли, а также линий передачи и приема данных, на плате присутствуют контакты, подписанные как RTS и CTS. Эти контакты используются для так называемого управления потоком — механизма, предназначенного для сигнализировании о готовности принимать данные со стороны ведущего или ведомого устройства в зависимости от состояния линий. Использование этих линий не является необходимостью, поэтому для отключения механизма необходимо указать в настройках драйвера компьютера что управление потоком не используется (обычно такая конфигурация является дефолтной).

При подключении к ПК в системе Windows контроллер UART определяется как последовательный виртуальный порт. Следовательно, разработка схемы взаимодействия с ПК сводится к созданию ПО с функционалом взаимодействия с последовательным виртуальным портом, а также к разработке проекта для ПЛИС, реализующего прием/передачу данных по интерфейсу UART.

Внешний контроллер UART по сути является абсолютным аналогом уже имеющегося на DE10-Nano контроллера, однако единственным его преимуществом является возможность непосредственного подключения к свободным контактам ПЛИС. Стоимость подобного устройства колеблется от $5 до $10.

Разработка ПО


Общая информация


Как уже было сказано, разработка ПО для ПК сводится к созданию программы, поддерживающей обмен информацией с виртуальным последовательным портом. В ходе анализа доступных программных средств для разработки ПО выбор пал на язык программирования Java 8-ой редакции c использованием библиотеки RTXT.

Java — это строго типизированный объектно-ориентированный язык программирования, обладающий рядом ключевых особенностей. В частности — программы, написанные на ЯП Java транслируются в специальный байт-код, что позволяет им выполняться на любой компьютерной архитектуре, для которой существует реализация виртуальной Java-машины.

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

В качестве инструмента для разработки пользовательского интерфейса использована старая, но надежная встроенная библиотека Swing. Благодаря возможности изменения тем, простой UI на Swing может выглядеть не уродливо более-менее современно.

Сама программа по-спартански простая и в основном использует документированные возможности библиотеки RTXT.

Основной функционал программы


Вот так происходит определение доступных портов:

    public String[] getPorts() {
        ports = CommPortIdentifier.getPortIdentifiers();
        while (ports.hasMoreElements()) {
            CommPortIdentifier curPort = (CommPortIdentifier) ports.nextElement();

            if (curPort.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                portMap.put(curPort.getName(), curPort);
            }
        }
        return portMap.keySet().toArray(new String[0]);
    }


Подключение к выбранному порту:

    public void connect() {
        String selectedPort = (String)gui.ports.getSelectedItem();
        selectedPortIdentifier = portMap.get(selectedPort);
        CommPort commPort;
        try{
            commPort = selectedPortIdentifier.open("UART controller", TIMEOUT);
            serialPort = (SerialPort)commPort;
            serialPort.setSerialPortParams(BAUD_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            gui.writeLog(selectedPort + " opened successfully.");
        }catch (PortInUseException e){
            gui.writeLogWithWarning(selectedPort + " is in use. (" + e.toString() + ")");
        }catch (Exception e){
            gui.writeLogWithWarning("Failed to open " + selectedPort + " (" + e.toString() + ")");
        }
    }


Процесс передачи данных:

    public boolean writeData(byte[] bytes){
        boolean successfull = false;
        try {
            gui.writeLog("WRITING: " + HexBinUtil.stringFromByteArray(bytes));
            output.write(bytes);
            output.flush();
            successfull = true;
        }
        catch (Exception e) {
            gui.writeLogWithWarning("Failed to write data. (" + e.toString() + ")");
        }
        return successfull;
    }


Прием данных:

    public void serialEvent(SerialPortEvent serialPortEvent) {
        if (serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            try {
                byte singleData = (byte)input.read();
                gui.writeLog("READING: " + HexBinUtil.stringFromByteArray(singleData));
            }
            catch (Exception e) {
                gui.writeLogWithWarning("Failed to read data. (" + e.toString() + ")");
            }
        }
    }


Как уже было сказано, управление ЦАП/АЦП происходит посредством передачи команды записи некоторого допустимого 8-битного значения по определенному адресу регистра, описанного в документации. Для определения текущего состояния регистра, необходимо передать команду чтения и указать адрес запрашиваемого регистра. Полное описание как всегда в документации.

Передача данных по UART


В ходе изучения AD9993-EBZ было выяснено, что в доступном 12-ти битном адресном пространстве регистров используются только 8 бит данных. Так же передаваемое значение состояния регистра описывается 8-мью битами. Для определения типа передачи данных (запись/чтение) требуется передать 1 бит данных.

Так как текущее ограничение на максимальный размер посылки по интерфейсу UART является 8 бит данных, было принято решение о передаче 3 последовательных посылок размером 8 бит при процессе записи и 2 посылки при процессе чтения.

Первая посылка будет содержать тип передачи данных, продублированной на весь размер посылки. Следовательно, возможных значений может быть всего два: 00000000 для чтения и 11111111 для записи соответственно. Использование целой посылки для передачи одного бита данных было сделано для упрощение дальнейшей обработки принятых сигналов.
Далее передается адрес запрашиваемого регистра и передаваемое значение состояния регистра при процессе записи.

ofqukpb2pkq7bc7uhd3tqmd8nxg.jpeg
Осциллограмма разрабатываемой посылки UART

Пользовательский интерфейс


lmk_mzftdzafn6564gpbnjm2puy.png

Основными элементами пользовательского интерфейса ПО являются кнопки подключения/отключения от доступных последовательных портов (элементы 3,4), поля ввода адреса и значения регистра (элементы 7,8), окно логов (элемент 6). ПО спроектировано с возможностью взаимодействия с ним в двух состояниях: «подключен к порту» и «отключен от порта». Состояния определяют активность некоторых элементов на интерфейсе программы, а также ограничивают возможность исполнения определенных методов с целью уменьшения вероятности ошибок и некорректного использования ПО. При включении программа находится в состоянии «отключен от порта». Так же в целях облегчения работы с программой (что и я являлось одной из главных целей проекта) был добавлен функционал подключения файлов с уже сохраненными командами в формате JSON (элемент 10).

Процесс загрузки команд:

itev1ztcswwoh1afqqxqifkyico.png
Выбор файла

bqvrwjz_kqv0v8idfgh50a0gdas.png
Интерфейс с командами

Функционал позволяет сортировать загруженные команды по разделам (элемент 12), настраивать процесс работы с подключенными командами (отправлять ли команду на последовательный порт сразу при нажатии, или заполнять поля адреса и значения в соответствии с выбранной командой).

Разработка проекта для ПЛИС


Общая информация


Прежде чем начать разработку проекта для ПЛИС, необходимо определиться с задачей, которую он обязан решить.

Звучит она примерно так: проект для ПЛИС должен поддерживать представленное ниже преобразование данных:

myrnu37djmbldjshymuyx2nuwuu.jpeg
Диаграмма преобразования данных

На вход DE10-Nano поступают сигналы с линии Tx интерфейса UART (верхний сигнал). Далее ПЛИС необходимо правильно определить принятые данные из трех посылок по 8 бит каждая и преобразовать их в соответствующий документации формат посылки SPI (представлен в 4 линиях).

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

При разработке проектов для ПЛИС использовалась IDE Qartus Prime Lite Edition 17-ой версии.
Если вы не имеете опыта использования Quartus или вообще не программировали ПЛИС, рекомендуется начать с понятного (по моему мнению) примера первого проекта на Quartus.

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

Контроллер UART


Описание


Контроллер UART состоит из трех модулей:

  • генератор импульсов;
  • модуль записи;
  • модуль чтения.


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

Генератор импульсов


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

Существует общепринятый ряд стандартных скоростей для UART: 300; 600; 1200; 2400; 4800; 9600; 19200; 38400; 57600; 115200; 230400; 460800; 921600 бод. В нашем случае передача данных происходит на скорости 9600 бод. Далее нам нужно получить генератор с частотой, в 16 раз превышающую скорость символов. Это необходимо для того, чтобы корректно детектировать уровень передаваемого сигнала.

Для генерирования импульсов используется имеющийся в чипе генератор с частотой 50 МГц. Чтобы получить искомую частоту, требуется учитывать каждый 325-ый импульс генератора.
Вот так это будет выглядеть на языке Verilog:

input           Clk;                   // генератор импульсов 50МГц
input           Rst_n;               // Сигнал сброса работы контроллера
input [15:0]    BaudRate;      // количество пропускаемых импульсов, равно 325
output          Tick;                // выходная линия
reg [15:0]      baudRateReg; // счетчик

always @(posedge Clk or negedge Rst_n)
    if (!Rst_n) baudRateReg <= 16'b1;
    else if (Tick) baudRateReg <= 16'b1;
         else baudRateReg <= baudRateReg + 1'b1;
assign Tick = (baudRateReg == BaudRate);
endmodule


Модуль чтения


Модуль чтения производит преобразование входного сигнала с линии RX в выходной 8-разрядный массив данных.

Непосредственно чтение и передача прочитанных данных на выход:

input Clk, Rx,Tick;  // входной генератор 50МГц, линия Rx, генератор опорных импульсов
input [3:0]NBits;                  // количество бит в одной посылке, равно 8
output RxDone;	         // сигнализирование о законченном процессе чтения
output [7:0]RxData;                         // прочитанные данные

reg  read_enable = 1'b0;                   // регистр разрешения чтения
reg  RxDone = 1'b0;                         // регистр окончания чтения
reg [3:0] counter = 4'b0000;            // счетчик опорных имульсов
reg  start_bit = 1'b1;	                      // регистр для детектирования старт бита
reg [4:0]Bit = 5'b00000;                  // текущее количество прочитанных битов
reg [7:0] RxData;                            // выходной массив прочитанных данных
reg [7:0] Read_data= 8'b00000000; // промежуточный массив прочитанных данных


always @ (posedge Tick)  // каждый раз при детектировании переднего фронта опорного сигнала
	begin
	if (read_enable)
	begin
	RxDone <= 1'b0;			//сбрасывание регистра конца передачи
	counter <= counter+1'b1;		//инкрементирование счетчика
	

	if ((counter == 4'b1000) & (start_bit))	// определение стартового бита
	begin
	start_bit <= 1'b0;
	counter <= 4'b0000;
	end

	if ((counter == 4'b1111) & (!start_bit) & (Bit < NBits)) //последующее чтении данных
	begin
	Bit <= Bit+1'b1;
	Read_data <= {Rx,Read_data[7:1]};
	counter <= 4'b0000;
	end
	
	if ((counter == 4'b1111) & (Bit == NBits)  & (Rx))  //определение стоп бита
	begin
	Bit <= 4'b0000;
	RxDone <= 1'b1;          // выставление значение регистра окончания чтения данных
	counter <= 4'b0000;          // сбрасывание счетчика
	start_bit <= 1'b1;		//сбрасывание регистра стартового бита
	end
	end
	
end


always @ (posedge Clk)
begin

if (NBits == 4'b1000)            //если записано 8 бит данных, передаем их на выход
begin
RxData[7:0] <= Read_data[7:0];	
end

end


Модуль передачи

Модуль передачи преобразует 8-разрядных входной сигнал в последовательный пакет данных стандарта UART.

Непосредственная передача данных:

input Clk, Rst_n, TxEn;  //генератор 50Гц, сигнал сброса, сигнал разрешения чтения
input Tick;                    //генератор опорных импульсов
input [3:0]NBits;          //количество бит в посылке
input [7:0]TxData;	

output Tx;
output TxDone;

reg  Tx;	
reg  TxDone = 1'b0;	
reg write_enable = 1'b0;
reg start_bit = 1'b1;	
reg stop_bit = 1'b0;	
reg [4:0] Bit = 5'b00000;	
reg [3:0] counter = 4'b0000;
reg [7:0] in_data=8'b00000000;

always @ (posedge Tick)  // каждый раз при детектировании переднего фронта опорного сигнала
begin

	if (!write_enable)	
	begin
	TxDone = 1'b0;
	start_bit <=1'b1;
	stop_bit <= 1'b0;
	end

	if (write_enable)
	begin
	counter <= counter+1'b1;	//инкрементирование счетчика
	
	if(start_bit & !stop_bit)//запись старт бита и передача записываемых данных в массив
	begin
	Tx <=1'b0;					
	in_data <= TxData;	

	if ((counter == 4'b1111) & (start_bit) )    //запись первого бита данных
	begin		
	start_bit <= 1'b0;
	in_data <= {1'b0,in_data[7:1]};
	Tx <= in_data[0];
	end

	if ((counter == 4'b1111) & (!start_bit) &  (Bit < NBits-1))	//запись последующих битов
	begin		
	in_data <= {1'b0,in_data[7:1]};
	Bit<=Bit+1'b1;
	Tx <= in_data[0];
	start_bit <= 1'b0;
	counter <= 4'b0000;
	end	

	if ((counter == 4'b1111) & (Bit == NBits-1) & (!stop_bit))	//запись стоп бита
	begin
	Tx <= 1'b1;	
	counter <= 4'b0000;	
	stop_bit<=1'b1;
	end

	if ((counter == 4'b1111) & (Bit == NBits-1) & (stop_bit) )	//сброс регистров
	begin
	Bit <= 4'b0000;
	TxDone <= 1'b1;
	counter <= 4'b0000;
	//start_bit <=1'b1;
	end
	
	end
		
end


Контроллер SPI


Важное отступление


Так как контроллер SPI, реализованный на базе ПЛИС, представляет собой более сложную логическую структуру, чем контроллер UART, то дальнейшее объяснение логики работы легче провести на схемотехнической модели контроллера.

Общая схема контроллера


uy77qyngbjuouotnfkhx_amwxec.png

Разработанную модель можно разделить на 3 основных модуля:

  • модуль последовательной записи;
  • модуль битового счетчика;
  • модуль анализатора ошибок.


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

Тактирование работы схемы обеспечивается сигналом частоты

© Habrahabr.ru