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

Проектирование FPGA-контроллера RC-сервопривода с точным PWM

Радиоуправляемые (RC) сервоприводы — сердце любительских самолетов, машин и лодок. Они преобразуют радиокоманду в точную угловую позицию. Поскольку RC-системы существуют десятилетиями, индустриальным стандартом интерфейса является широтно-импульсная модуляция (PWM), а не чистый цифровой протокол. Благодаря детерминированному таймингу, который предоставляет FPGA, реализация высокоточного PWM становится простой. Эта статья проводит через универсальный VHDL-контроллер сервопривода, работающий с любым RC-сервоприводом через PWM, включая симуляционный тестбенч и реальную реализацию на плате Lattice iCEstick FPGA.

Как работает PWM-управление для RC-сервопривода

Основы PWM рассматриваются в других местах, но RC-сервоприводы требуют немного иного подхода. Сервопривод игнорирует полный коэффициент заполнения; он считывает только длительность высокого уровня каждого импульса. Стандартный интервал между импульсами составляет 20 мс, что соответствует частоте 50 Гц. В этом 20-мс окне сервопривод считывает импульс, ширина которого обычно варьируется от 1 мс (минимальная позиция) до 2 мс (максимальная позиция), хотя точные значения зависят от производителя.

Проектирование FPGA-контроллера RC-сервопривода с точным PWM

Рисунок иллюстрирует временную связь: период 20 мс повторяется, а длительность высокого уровня каждого импульса напрямую отображается на угловую позицию сервопривода.

VHDL-контроллер сервопривода

Мы создадим универсальный VHDL-модуль, принимающий generic-параметры для частоты тактового сигнала, частоты PWM-импульса, минимальной и максимальной ширины импульса (в микросекундах) и количества дискретных позиций между экстремумами. Поскольку тактовый сигнал FPGA работает в мегагерцах, целочисленная счетная арифметика дает нам достаточную разрешающую способность. Чтобы избежать неопределенностей округления, используем функцию round из пакета ieee.math_real, которая всегда округляет от нуля.

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.round;

Ниже приведено объявление сущности модуля сервопривода:

entity servo is generic ( clk_hz : real; pulse_hz : real; -- PWM pulse frequency min_pulse_us : real; -- микросекунды в минимальной позиции max_pulse_us : real; -- микросекунды в максимальной позиции step_count : positive -- Количество дискретных позиций ); port ( clk : in std_logic; rst : in std_logic; position : in integer range 0 to step_count - 1; pwm : out std_logic ); end servo;

Вход position управляет сервоприводом от минимума (0) до максимума (step_count‑1). Выход pwm подключается к сигнальному проводу сервопривода (обычно желтый или белый). Большинство FPGA выдают логику 3.3 В, поэтому для согласования с 5-вольтовым входом сервопривода требуется преобразователь уровней.

Декларативная область

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

function cycles_per_us (us_count : real) return integer is begin return integer(round(clk_hz / 1.0e6 * us_count)); end function;

Используя эту функцию, вычисляем основные временные константы:

constant min_count : integer := cycles_per_us(min_pulse_us); constant max_count : integer := cycles_per_us(max_pulse_us); constant min_max_range_us : real := max_pulse_us - min_pulse_us; constant step_us : real := min_max_range_us / real(step_count - 1); constant cycles_per_step : positive := cycles_per_us(step_us);

Далее настраиваем PWM-счетчик, работающий на нужной частоте импульсов:

constant counter_max : integer := integer(round(clk_hz / pulse_hz)) - 1; signal counter : integer range 0 to counter_max; signal duty_cycle : integer range 0 to max_count;

Подсчет тактовых циклов

Следующий процесс реализует свободно бегущий счетчик:

COUNTER_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then counter <= 0; else if counter < counter_max then counter <= counter + 1; else counter <= 0; end if; end if; end if; end process;

Процесс PWM-выхода

PWM-выход устанавливается в высокий уровень на столько счетных циклов, сколько определяет текущее значение duty_cycle:

PWM_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then pwm <= '0'; else pwm <= '0'; if counter < duty_cycle then pwm <= '1'; end if; end if; end if; end process;

Вычисление коэффициента заполнения

При сбросе duty_cycle по умолчанию устанавливается в минимальную ширину импульса. Иначе он вычисляется из входа position:

DUTY_CYCLE_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then duty_cycle <= min_count; else duty_cycle <= position * cycles_per_step + min_count; end if; end if; end process;

Хотя cycles_per_step округляется до ближайшего целого, получающаяся погрешность пренебрежимо мала, так как тактовый сигнал FPGA на порядки быстрее 50-Гц частоты импульсов.

Тестбенч сервопривода

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

Константы симуляции

constant clk_hz : real := 1.0e6; constant clk_period : time := 1 sec / clk_hz; constant pulse_hz : real := 50.0; constant pulse_period : time := 1 sec / pulse_hz; constant min_pulse_us : real := 1000.0; constant max_pulse_us : real := 2000.0; constant step_count : positive := 5;

Сигналы DUT

signal clk : std_logic := '1'; signal rst : std_logic := '1'; signal position : integer range 0 to step_count - 1; signal pwm : std_logic;

Генерация тактового сигнала:

clk <= not clk after clk_period / 2;

Инстанциация DUT

DUT : entity work.servo(rtl) generic map ( clk_hz => clk_hz, pulse_hz => pulse_hz, min_pulse_us => min_pulse_us, max_pulse_us => max_pulse_us, step_count => step_count ) port map ( clk => clk, rst => rst, position => position, pwm => pwm );

Последователь тестбенча

Последователь сбрасывает DUT, затем перебирает все значения позиции, ожидая один период импульса между изменениями. После завершения последовательности симуляция заканчивается.

SEQUENCER : process begin wait for 10 * clk_period; rst <= '0'; wait for pulse_period; for i in 0 to step_count - 1 loop position <= i; wait for pulse_period; end loop; report "Simulation done. Check waveform."; finish; end process;

Волновая форма симуляции сервопривода

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

Проектирование FPGA-контроллера RC-сервопривода с точным PWM

Скачайте полные файлы проекта для воспроизведения симуляции.

Пример FPGA-реализации

Теперь будем управлять реальным сервоприводом TowerPro SG90 с платы Lattice iCEstick. Сервопривод требует логический уровень 5 В, поэтому используем преобразователь уровней между выводом FPGA и сигнальным проводом сервопривода.

Сущность топ-модуля

entity top is port ( clk : in std_logic; rst_n : in std_logic; -- Pull‑up pwm : out std_logic ); end top;

Сигналы и константы

Мы откалибровали ширины импульсов сервопривода экспериментально: 0.5 мс – 2.5 мс дают полный размах 180°. Следующие константы соответствуют тактовому сигналу iCEstick и параметрам сервопривода.

constant clk_hz : real := 12.0e6; -- iCEstick clock constant pulse_hz : real := 50.0; constant min_pulse_us : real := 500.0; -- 0.5 ms constant max_pulse_us : real := 2500.0; -- 2.5 ms constant step_bits : positive := 8; -- 0 to 255 constant step_count : positive := 2**step_bits;

Свободно бегущий счетчик имеет ширину 25 бит, давая время оборачивания примерно 2.8 секунд на 12 МГц такте.

constant cnt_bits : integer := 25; signal cnt : unsigned(cnt_bits - 1 downto 0);

Соединение модулей:

signal rst : std_logic; signal position : integer range 0 to step_count - 1; signal rom_addr : unsigned(step_bits - 1 downto 0); signal rom_data : unsigned(step_bits - 1 downto 0);

Инстанциация модуля сервопривода

SERVO : entity work.servo(rtl) generic map ( clk_hz => clk_hz, pulse_hz => pulse_hz, min_pulse_us => min_pulse_us, max_pulse_us => max_pulse_us, step_count => step_count ) port map ( clk => clk, rst => rst, position => position, pwm => pwm );

Инстанциация самозавершающегося счетчика

COUNTER : entity work.counter(rtl) generic map ( counter_bits => cnt_bits ) port map ( clk => clk, rst => rst, count_enable => '1', counter => cnt );

Инстанциация Sine ROM

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

Параллельные присваивания связывают счетчик, ROM и сервопривод вместе:

position <= to_integer(rom_data); rom_addr <= cnt(cnt'left downto cnt'left - step_bits + 1);

Тестирование на Lattice iCEstick

Вся схема собрана на макетной плате: 3.3-вольтовый выход FPGA преобразуется в 5 В для сервопривода. PWM-сигнал подключен к выводу 119 на iCEstick. При включении сервопривод плавно качается между 0° и 180°, кратковременно останавливаясь на краях. Видео волновой формы PWM на осциллографе демонстрирует правильный тайминг.

Проектирование FPGA-контроллера RC-сервопривода с точным PWM

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

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

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

Читайте об аналоговом управлении питанием с PWM здесь:
Как создать PWM-контроллер в VHDL

Готовы попробовать сами? Скачайте ZIP-архив с полным VHDL-кодом, тестбенчем ModelSim, проектом iCEcube2 и конфигурацией программатора, введя свой email ниже.

Дайте знать, что думаете, в комментариях!

VHDL

  1. PWM контроллер мощности: сборка драйвера лампы с пульсирующей шириной модуляции
  2. Реализация PWM-контроллера в VHDL: проектирование, симуляция и FPGA-демо
  3. Инициализация блочной памяти FPGA из текстовых файлов с помощью библиотеки TEXTIO в VHDL
  4. Потоковая передача данных температуры с ppDAQC Pi-Plate в InitialState на headless Raspberry Pi
  5. Мониторинг домашней температуры с панелью управления на Raspberry Pi
  6. Пошаговое руководство: Извлечение данных ПЛК с помощью ППИоТ для получения инсайтов в реальном времени
  7. Защита внутреннего ИИ с доверенной средой выполнения на защищённом FPGA SoC
  8. Могут ли ваши серво-контроллеры быть отремонтированы? Экспертные инсайты и экономичные решения
  9. Устранение ошибки «C Axis Drive Not OK» на сервоприводе Osai-Prima Electro 97942050N
  10. Управление 4-пиновым PWM-вентилятором 25 кГц на Arduino Uno — Сборка, код и тесты