Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> VHDL

Создание эффекта «дыхания» светодиода с помощью синусоиды из блочной памяти

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

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

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

Создание эффекта «дыхания» светодиода с помощью синусоиды из блочной памяти

Эта статья — продолжение прошлогодней публикации о широтно-импульсной модуляции (PWM). Сегодня мы используем PWM-модуль, пилообразный счетчик и модуль сброса, созданные в предыдущем уроке. Нажмите ссылку ниже, чтобы прочитать статью о PWM!

Как создать PWM-контроллер в VHDL

Хранение значений синусоиды в блочной памяти

Хотя можно генерировать синусоиду с помощью DSP-примитивов в FPGA, более простой способ — хранить отсчеты в блочной памяти. Вместо вычисления значений во время выполнения, мы вычисляем серию значений синуса во время синтеза и создаем только для чтения память (ROM) для их хранения.

Рассмотрим минимальный пример ниже, показывающий, как сохранить полную синусоиду в ROM 3×3 бита. И адресный вход, и выход данных имеют ширину 3 бита, то есть могут представлять целое значение в диапазоне 0–7. Мы можем хранить восемь значений синуса в ROM, а разрешение данных также 0–7.

Создание эффекта «дыхания» светодиода с помощью синусоиды из блочной памяти

Тригонометрическая функция синус выдает число в диапазоне [‑1, 1] для любого входного угла x. Цикл повторяется при x ≥ 2π. Поэтому достаточно хранить только значения синуса от нуля до, но не включая 2π. Значение синуса для 2π то же, что и для нуля. Иллюстрация выше демонстрирует эту концепцию. Мы храним значения синуса от нуля до 7π/4, что является последним равномерным шагом перед полным кругом 2π.

Цифровая логика не может представлять вещественные значения, как угол или значения синуса, с бесконечной точностью. Это справедливо для любой компьютерной системы. Даже при использовании чисел с плавающей точкой двойной точности это лишь приближение. Так работают двоичные числа, и наш синус-ROM не исключение.

Чтобы максимально использовать доступные биты данных, мы добавляем смещение и масштаб к значениям синуса при вычислении содержимого ROM. Наименьшее возможное значение синуса -1 отображается в значение данных 0, а наибольшее возможное значение синуса 1 преобразуется в 2^{data_bits-1}, как показывает формула ниже.

\mathit{data} = \mathit{Round}\left(\frac{(1 + \sin \mathit{addr}) * (2^\mathit{data\_bits} - 1)}{2}\right)

Для преобразования адреса ROM в угол x можно использовать формулу:

x = \frac{\mathit{addr} * 2\pi}{2^\mathit{addr\_bits}}

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

Модуль Sine ROM

Модуль Sine ROM, представленный в этой статье, выводит блочную память на большинстве FPGA-архитектур. Рассмотрите возможность согласования generic-параметров ширины и глубины с примитивами памяти вашей целевой FPGA. Это даст лучшее использование ресурсов. Вы всегда можете обратиться к примеру проекта Lattice, если не уверены, как использовать Sine ROM для реального FPGA-проекта.

Оставьте свой email в форме ниже, чтобы скачать VHDL-файлы и проекты ModelSim / iCEcube2!

Сущность

Код ниже показывает сущность модуля Sine ROM. Он содержит два generic-входа, позволяющих задать ширину и глубину выводимой блочной памяти. Я использую спецификаторы диапазона на константах для предотвращения непреднамеренного целочисленного переполнения. Об этом позже в статье.

 entity sine_rom is generic ( addr_bits : integer range 1 to 30; data_bits : integer range 1 to 31 ); port ( clk : in std_logic; addr : in unsigned(addr_bits - 1 downto 0); data : out unsigned(data_bits - 1 downto 0) ); end sine_rom; 

Объявление порта имеет тактовый вход, но нет сброса, потому что примитивы памяти не могут иметь сброс. Вход addr — это место, где мы присваиваем масштабированное значение угла (x), а выход data — где появится масштабированное значение синуса.

Объявления типов

В начале декларативной области VHDL-файла мы объявляем тип и подтип для нашего объекта хранения ROM. Подтип addr_range — это целочисленный диапазон, равный количеству слотов в нашей памяти, а rom_type описывает двумерный массив, хранящий все значения синуса.

 subtype addr_range is integer range 0 to 2**addr_bits - 1; type rom_type is array (addr_range) of unsigned(data_bits - 1 downto 0); 

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

Обратите внимание, что мы используем generic addr_bits как основу для определения addr_range. Поэтому я ограничил максимальное значение addr_bits до 30. Потому что для больших значений вычисление 2**addr_bits - 1 вызовет переполнение. VHDL-целые 32-битные, хотя это изменится с VHDL-2019, использующим 64-битные целые. Но пока мы должны жить с этим ограничением при использовании целых в VHDL, пока инструменты не начнут поддерживать VHDL-2019.

Функция генерации значений синуса

Код ниже показывает функцию init_rom, генерирующую значения синуса. Она возвращает объект rom_type, поэтому мы должны сначала объявить тип, потом функцию, и наконец константу ROM. Они зависят друг от друга именно в этом порядке.

 function init_rom return rom_type is variable rom_v : rom_type; variable angle : real; variable sin_scaled : real; begin for i in addr_range loop angle := real(i) * ((2.0 * MATH_PI) / 2.0**addr_bits); sin_scaled := (1.0 + sin(angle)) * (2.0**data_bits - 1.0) / 2.0; rom_v(i) := to_unsigned(integer(round(sin_scaled)), data_bits); end loop; return rom_v; end init_rom; 

Функция использует несколько удобных переменных, включая rom_v, локальную копию массива, которую мы заполняем значениями синуса. Внутри подпрограммы мы используем цикл for для итерации по адресному диапазону, и для каждого слота ROM вычисляем значение синуса по формулам, описанным ранее. В конце возвращаем переменную rom_v, которая к этому моменту содержит все отсчеты синуса.

Целочисленное преобразование в последней строке цикла — причина, почему мне пришлось ограничить generic data_bits до 31 бита. Для больших битовых длин произойдет переполнение.

 constant rom : rom_type := init_rom; 

Под определением функции init_rom мы объявляем объект rom как константу. ROM — это просто RAM, в которую никогда не пишут, так что это нормально. И теперь время использовать нашу функцию. Мы вызываем init_rom для генерации начальных значений, как показано в коде выше.

Процесс ROM

Единственная логика в файле Sine ROM — довольно простой процесс, приведенный ниже. Он описывает блочную память с одним портом чтения.

 ROM_PROC : process(clk) begin if rising_edge(clk) then data <= rom(to_integer(addr)); end if; end process; 

Топ-модуль

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

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

Создание эффекта «дыхания» светодиода с помощью синусоиды из блочной памяти

Код ниже показывает декларативную область топ-файла VHDL. В прошлогоднем PWM-дизайне объект duty_cycle был псевдонимом для старших бит счетчика cnt. Но это не сработает сейчас, потому что выход из Sine ROM будет управлять коэффициентом заполнения, поэтому я заменил его на реальный сигнал. Более того, я создал новый псевдоним с именем addr, который представляет старшие биты счетчика. Мы будем использовать его как адресный вход для ROM.

 signal rst : std_logic; signal cnt : unsigned(cnt_bits - 1 downto 0); signal pwm_out : std_logic; signal duty_cycle : unsigned(pwm_bits - 1 downto 0); -- Use MSBs of counter for sine ROM address input alias addr is cnt(cnt'high downto cnt'length - pwm_bits); 

Вы можете увидеть, как инстанцировать наш новый Sine ROM в топ-модуле в листинге ниже. Мы задаем ширину и глубину памяти в соответствии с длиной внутреннего счетчика PWM-модуля. Выход data из ROM управляет входом duty_cycle PWM-модуля. Значение на сигнале duty_cycle будет изображать синусоидальный паттерн, когда мы считываем слоты RAM один за другим.

 SINE_ROM : entity work.sine_rom(rtl) generic map ( data_bits => pwm_bits, addr_bits => pwm_bits ) port map ( clk => clk, addr => addr, data => duty_cycle ); 

Симуляция Sine Wave ROM

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

Создание эффекта «дыхания» светодиода с помощью синусоиды из блочной памяти

Именно выход led_5 верхнего уровня несет PWM-сигнал, управляющий внешним светодиодом. Видно, что выход быстро меняется, когда коэффициент заполнения растет или падает. Но когда коэффициент заполнения на вершине синусоиды, led_5 — стабильная «1». Когда волна внизу склона, выход кратковременно остается на «0».

Хотите попробовать на домашнем компьютере? Введите свой email в форме ниже, чтобы получить VHDL-файлы и проекты ModelSim и iCEcube2!

Реализация «дыхания» светодиода на FPGA

Я использовал программное обеспечение Lattice iCEcube2 для реализации дизайна на плате iCEstick FPGA. Используйте форму выше, чтобы скачать проект и попробовать на своей плате, если у вас есть iCEstick!

Листинг ниже показывает использование ресурсов, согласно отчету ПО Synplify Pro, входящего в iCEcube2. Показывает, что дизайн использует один примитив блочной памяти. Это наш Sine ROM.

 Resource Usage Report for led_breathing Mapping to part: ice40hx1ktq144 Cell usage: GND 4 uses SB_CARRY 31 uses SB_DFF 5 uses SB_DFFSR 39 uses SB_GB 1 use SB_RAM256x16 1 use VCC 4 uses SB_LUT4 65 uses I/O ports: 7 I/O primitives: 7 SB_GB_IO 1 use SB_IO 6 uses I/O Register bits: 0 Register bits not including I/Os: 44 (3%) RAM/ROM usage summary Block Rams : 1 of 16 (6%) Total load per clock: led_breathing|clk: 1 @S |Mapping Summary: Total LUTs: 65 (5%) 

После трассировки дизайна в iCEcube2 вы найдете файл .bin в папке led_breathing_Implmnt\sbt\outputs\bitmap внутри директории проекта Lattice_iCEcube2_proj.

Можете использовать standalone-программатор Lattice Diamond для программирования FPGA, как показано в руководстве пользователя iCEstick. Я сделал именно так, и GIF-анимация ниже показывает результат. Интенсивность света светодиода осциллирует с синусоидальным паттерном. Это выглядит очень естественно, и светодиод кажется «дышащим», если включить немного воображения.

Создание эффекта «дыхания» светодиода с помощью синусоиды из блочной памяти

Заключительные мысли

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

Первая причина, приходящая на ум — 32-битный предел для целочисленных значений, который я обсуждал ранее. Уверен, вы можете преодолеть эту проблему, вычисляя значение синуса хитрее, но это не все.

На каждый бит, которым вы расширяете адрес ROM, вы удваиваете использование памяти. Если вам нужна высокая точность по оси X, может не хватить памяти даже на большей FPGA.

Если количество бит, используемых для значений синуса по оси Y, превышает нативную глубину памяти, инструмент синтеза будет использовать дополнительные RAM или LUT для реализации ROM. Это съест больше вашего ресурсного бюджета при увеличении точности по Y.

Теоретически нам нужно хранить только один квадрант синусоиды. Поэтому можно обойтись четвертью памяти, если использовать конечный автомат (FSM) для управления чтением ROM. Он должен инвертировать квадрант синуса для всех четырех перестановок осей X и Y. Тогда можно построить полную синусоиду из единственного квадранта, хранящегося в блочной памяти.

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

VHDL

  1. Создайте свой первый шаблон AWS CloudFormation — практическое руководство
  2. Создание бесшовного пользовательского опыта: чертеж UX-дизайна
  3. Создание списков строк в VHDL: лучшие практики и пример
  4. Создание Tcl-управляемого тестбенча для VHDL-модуля кодового замка
  5. Инициализация блочной памяти FPGA из текстовых файлов с помощью библиотеки TEXTIO в VHDL
  6. Как создать самопроверяющийся тестбенч в VHDL — практическое руководство
  7. Реализация динамического связного списка в VHDL с защищенными типами и указателями доступа
  8. Создание массива объектов в Java: пошаговое руководство
  9. Цифровое производство революционизирует анатомическое моделирование в современной медицине
  10. Вызов функциональных блоков ПЛК через OPC UA клиентов: Практическое руководство