Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> VHDL

Projetando um Controlador de Servo RC Baseado em FPGA com PWM Preciso

Servos de radiocontrole (RC) são o coração de aeronaves, carros e barcos de hobby. Eles traduzem um comando de rádio remoto em uma posição angular precisa. Como sistemas RC existem há décadas, a interface padrão da indústria é modulação por largura de pulso (PWM) em vez de um protocolo puramente digital. Com o tempo determinístico que um field-programmable gate array (FPGA) oferece, implementar PWM de alta precisão é simples. Este artigo percorre um controlador de servo VHDL genérico que funciona com qualquer servo RC usando PWM, incluindo um testbench de simulação e uma implementação real em uma placa FPGA Lattice iCEstick.

Como o Controle PWM para um Servo RC Funciona

Embora os fundamentos de PWM sejam abordados em outro lugar, servos RC exigem uma abordagem ligeiramente diferente. O servo ignora o ciclo de trabalho completo; ele amostra apenas a duração do período alto de cada pulso. O intervalo padrão entre pulsos é 20 ms, o que corresponde a uma frequência de 50 Hz. Dentro dessa janela de 20 ms, o servo lê um pulso cuja largura tipicamente varia de 1 ms (posição mínima) a 2 ms (posição máxima), embora os valores exatos variem por fabricante.

Projetando um Controlador de Servo RC Baseado em FPGA com PWM Preciso

A figura ilustra a relação de tempo: um período de 20 ms se repete, e o tempo alto de cada pulso mapeia diretamente para a posição angular do servo.

O Controlador de Servo VHDL

Construiremos um módulo VHDL genérico que aceita parâmetros genéricos para frequência de clock, frequência de pulso PWM, larguras mínima e máxima de pulso (em microssegundos) e o número de posições discretas entre os extremos. Como o clock do FPGA roda na faixa de megahertz, aritmética de contador inteiro nos dá resolução suficiente. Para evitar ambiguidades de arredondamento, usaremos a função round do pacote ieee.math_real, que sempre arredonda para longe de zero.

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

Abaixo está a declaração da entidade para o módulo servo:

entity servo is generic ( clk_hz : real; pulse_hz : real; -- PWM pulse frequency min_pulse_us : real; -- microseconds at minimum position max_pulse_us : real; -- microseconds at maximum position step_count : positive -- Number of discrete positions ); port ( clk : in std_logic; rst : in std_logic; position : in integer range 0 to step_count - 1; pwm : out std_logic ); end servo;

A entrada position aciona o servo do mínimo (0) ao máximo (step_count-1). A saída pwm conecta ao fio de sinal do servo (geralmente amarelo ou branco). A maioria dos FPGAs gera lógica de 3.3 V, então um conversor de nível é necessário para corresponder à entrada de 5 V do servo.

A Região Declarativa

Começamos definindo uma função auxiliar que converte um tempo em microssegundos para o número inteiro mais próximo de ciclos de clock:

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

Usando essa função, calculamos as constantes de tempo principais:

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);

Em seguida, configuramos o contador PWM que roda na frequência de pulso desejada:

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;

Contando Ciclos de Clock

O processo a seguir implementa o contador free-running:

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;

Processo de Saída PWM

A saída PWM é mantida alta por tantos ciclos de contador quanto o valor atual de duty_cycle ditar:

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;

Calculando o Ciclo de Trabalho

Quando em reset, duty_cycle assume como padrão a largura mínima de pulso. Caso contrário, é calculado a partir da entrada 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;

Embora cycles_per_step seja arredondado para o inteiro mais próximo, o erro resultante é insignificante porque o clock do FPGA é ordens de magnitude mais rápido que a taxa de pulso de 50 Hz.

O Testbench do Servo

Para verificar o projeto, construímos um testbench simples que percorre manualmente todos os valores de posição e captura a forma de onda. Um clock de teste de 1 MHz acelera a simulação enquanto ainda reflete tempos realistas.

Constantes de Simulação

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;

Sinais do DUT

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

Geração de clock:

clk <= not clk after clk_period / 2;

Instanciação do 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 );

Sequenciador do Testbench

O sequenciador reseta o DUT, depois itera por todos os valores de posição, esperando um período de pulso entre mudanças. Após completar a sequência, a simulação termina.

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;

Forma de Onda da Simulação do Servo

O snapshot a seguir mostra a forma de onda do testbench. A saída PWM fica alta apenas pelos primeiros ciclos de contador, correspondendo à largura de pulso esperada para cada valor de posição.

Projetando um Controlador de Servo RC Baseado em FPGA com PWM Preciso

Baixe os arquivos completos do projeto para reproduzir a simulação.

Implementação de Exemplo em FPGA

Em seguida, acionamos um servo TowerPro SG90 real a partir de uma placa Lattice iCEstick. O servo requer nível lógico de 5 V, então usamos um conversor de nível entre o pino do FPGA e o fio de sinal do servo.

Entidade do Módulo Topo

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

Sinais e Constantes

Calibramos as larguras de pulso do servo experimentalmente: 0.5 ms a 2.5 ms resulta em uma varredura completa de 180°. As constantes a seguir correspondem ao clock do iCEstick e parâmetros do servo.

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;

O contador free-running tem 25 bits de largura, dando um tempo de wrap de aproximadamente 2.8 segundos no clock de 12 MHz.

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

Conectando os módulos:

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);

Instanciação do Módulo Servo

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 );

Instanciação do Contador Auto-Wrapping

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

Instanciação da ROM de Seno

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 );

Atribuições concorrentes conectam o contador, ROM e servo:

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

Teste na Lattice iCEstick

O circuito completo é montado em protoboard: a saída de 3.3 V do FPGA é convertida para 5 V para o servo. O sinal PWM conecta ao pino 119 no iCEstick. Quando alimentado, o servo oscila suavemente entre 0° e 180°, pausando brevemente nos extremos. Um vídeo da forma de onda PWM no osciloscópio demonstra o tempo correto.

Projetando um Controlador de Servo RC Baseado em FPGA com PWM Preciso

Considerações Finais

Existem muitas maneiras de implementar um controlador de servo PWM em VHDL, mas usar contadores inteiros mantém a lógica sintetizada simples — apenas contadores, registradores e multiplexadores. A armadilha mais comum é overflow silencioso em aritmética de 32 bits, então sempre verifique se cálculos intermediários permanecem dentro dos limites para seus parâmetros escolhidos.

Note que este estilo PWM é adaptado para servos RC. Para aplicações onde o ciclo de trabalho importa mais que a frequência (ex: controle de potência analógico), uma estratégia PWM diferente é preferível.

Leia sobre controle de potência analógico usando PWM aqui:
Como criar um controlador PWM em VHDL

Pronto para testar você mesmo? Baixe o arquivo ZIP contendo o código VHDL completo, testbench ModelSim, projeto iCEcube2 e configuração do programador inserindo seu endereço de e-mail abaixo.

Deixe sua opinião nos comentários!

VHDL

  1. Controlador de Potência PWM: Construa um Driver de Lâmpada com Modulação de Largura de Pulso
  2. Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA
  3. Inicializando Block RAM de FPGA a partir de Arquivos de Texto Usando a Biblioteca TEXTIO do VHDL
  4. Transmissão de Dados de Temperatura em Tempo Real do ppDAQC Pi-Plate para InitialState com Raspberry Pi Headless
  5. Monitore a Temperatura da Sua Casa com Dashboard no Raspberry Pi
  6. Guia Passo a Passo: Extraindo Dados de CLP com IIoT para Insights em Tempo Real
  7. Protegendo IA Embarcada com Ambiente de Execução Confiável em SoC FPGA Seguro
  8. Seus Controladores Servo Podem Ser Reparados? Insights de Especialistas e Soluções Econômicas
  9. Resolvendo Erro 'C Axis Drive Not OK' no Servo Drive Osai-Prima Electro 97942050N
  10. Controle de Ventoinha PWM 4 Pinos 25 kHz com Arduino Uno – Montagem, Código e Teste