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

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Modulação por largura de pulso (PWM) transforma um pino digital de FPGA em um poderoso controlador analógico. Alternando a alimentação entre ligado e desligado em alta velocidade, o PWM entrega a corrente média exata que um dispositivo precisa, sem nunca gerar uma tensão analógica verdadeira.

Aplicações típicas de PWM incluem saída de áudio para alto-falantes, dimerização de LEDs e controle de torque de motores de indução como servomotores, ventiladores de computador, bombas e motores DC brushless para veículos elétricos.

Veja também: Controlador de servo RC usando PWM de pino FPGA

Como o PWM Funciona

Quando uma fonte de alimentação é alternada entre 100% e 0% em alta frequência, a corrente média vista pela carga é diretamente proporcional à fração de tempo que a alimentação fica ligada. O diagrama a seguir ilustra o princípio:

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Ciclo de Trabalho

O ciclo de trabalho define por quanto tempo a saída PWM permanece alta durante cada período. Expresso como porcentagem, é calculado a partir do valor do contador binário:

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Em uma implementação VHDL, tratamos o ciclo de trabalho como um inteiro sem sinal, nos dando a resolução total do contador PWM. Um ciclo de trabalho de 0% desliga a saída permanentemente, enquanto 100% a mantém continuamente ligada. A resolução alcançável é determinada pela largura do contador.

Para converter o valor binário de ciclo de trabalho para porcentagem, use:

\mathit{duty\_cycle\_percentage} = \frac{\mathit{commanded\_duty\_cycle} * 100}{2^\mathit{pwm\_bits} - 1}

Frequência PWM

A frequência PWM é o inverso do período:

\mathit{pwm\_freq} = \frac{1}{\mathit{pwm\_period}}

Escolher a frequência certa depende do dispositivo alvo. Para LEDs, algumas centenas de hertz são suficientes para evitar flicker visível. Motores DC brushless, por outro lado, tipicamente operam na faixa de dezenas de quilohertz; frequência muito baixa pode produzir vibrações mecânicas, enquanto frequência muito alta desperdiça potência.

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Como as chaves MOSFET são mais lentas que a lógica do FPGA, operar o PWM muito rápido pode fazer o transistor passar tempo em um estado parcialmente condutor, dissipando calor extra.

Módulo Gerador PWM

Abaixo está uma implementação VHDL genérica de um controlador PWM que pode ser instanciado com qualquer largura de contador e fator de divisão de clock.

Declaração da Entidade

 entity pwm is generic ( pwm_bits : integer; clk_cnt_len : positive := 1 ); port ( clk : in std_logic; rst : in std_logic; duty_cycle : in unsigned(pwm_bits - 1 downto 0); pwm_out : out std_logic ); end pwm; 

As portas clk e rst fornecem controle síncrono. A entrada duty_cycle tem largura definida por pwm_bits, então a resolução do contador é diretamente configurável. O sinal pwm_out é roteado para o pino do FPGA que aciona o gate do MOSFET.

Sinais Internos

 signal pwm_cnt : unsigned(pwm_bits - 1 downto 0); signal clk_cnt : integer range 0 to clk_cnt_len - 1; 

O contador pwm_cnt espelha a largura do ciclo de trabalho. O contador clk_cnt implementa um divisor de clock opcional; definir clk_cnt_len como 1 desabilita o divisor e remove a lógica associada.

Processo Divisor de Clock

 CLK_CNT_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then clk_cnt <= 0; else if clk_cnt < clk_cnt_len - 1 then clk_cnt <= clk_cnt + 1; else clk_cnt <= 0; end if; end if; end if; end process; 

Com o padrão clk_cnt_len = 1, ferramentas de síntese eliminarão este processo automaticamente.

Processo de Saída PWM

 PWM_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then pwm_cnt <= (others => '0'); pwm_out <= '0'; else if clk_cnt_len = 1 or clk_cnt = 0 then pwm_cnt <= pwm_cnt + 1; pwm_out <= '0'; if pwm_cnt = unsigned(to_signed(-2, pwm_cnt'length)) then pwm_cnt <= (others => '0'); end if; if pwm_cnt < duty_cycle then pwm_out <= '1'; end if; end if; end if; end if; end process; 

O contador incrementa apenas quando clk_cnt é zero, efetivamente controlando a frequência PWM. A condicional que reseta pwm_cnt no seu segundo maior valor garante que um ciclo de trabalho de 100% seja alcançável.

\mathit{pwm\_hz} = \frac{\mathit{clk\_hz}}{(2^\mathit{pwm\_bits} - 1) * \mathit{clk\_cnt\_len}}

Projeto Top-Level

Para demonstrar o controlador PWM, o projeto aciona o LED de power-on em uma FPGA Lattice iCEstick. O oscilador de 12 MHz da placa alimenta um contador que controla a intensidade do LED em padrão dente de serra.

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Declaração da Entidade

 entity pwm_led is generic ( pwm_bits : integer := 8; cnt_bits : integer := 25; clk_cnt_len : positive := 47 ); port ( clk : in std_logic; rst_n : in std_logic; led_1 : out std_logic; led_2 : out std_logic; led_3 : out std_logic; led_4 : out std_logic; led_5 : out std_logic ); end pwm_led; 

O clock de 12 MHz e os genéricos escolhidos produzem uma frequência PWM de aproximadamente 1 kHz:
\frac{12e6}{(2^8 - 1) * 47} \approx 1 \text{ kHz}

O genérico cnt_bits aciona um contador dente de serra de 25 bits que controla o ciclo de trabalho do LED. Seu período é cerca de 2.8 Hz:

\frac{2^{\mathit{cnt\_bits}}}{\mathit{clk\_hz}} = \frac{2^{25}}{12e6} \approx 2.8 \text{ Hz}

Apenas o LED 5 é usado; os outros LEDs são amarrados a baixo para evitar iluminação indesejada.

Sinais Internos e Conexões

 architecture str of pwm_led is signal rst : std_logic; signal cnt : unsigned(cnt_bits - 1 downto 0); signal pwm_out : std_logic; alias duty_cycle is cnt(cnt'high downto cnt'length - pwm_bits); begin led_1 <= '0'; led_2 <= '0'; led_3 <= '0'; led_4 <= '0'; led_5 <= pwm_out; 

O sinal rst é uma versão sincronizada, não invertida do reset externo. O alias duty_cycle mapeia os bits mais significativos de cnt para o controlador PWM.

Instanciações dos Módulos

 RESET : entity work.reset(rtl) port map ( clk => clk, rst_n => rst_n, rst => rst ); PWM : entity work.pwm(rtl) generic map ( pwm_bits => pwm_bits, clk_cnt_len => clk_cnt_len ) port map ( clk => clk, rst => rst, duty_cycle => duty_cycle, pwm_out => pwm_out ); COUNTER : entity work.counter(rtl) generic map ( counter_bits => cnt'length ) port map ( clk => clk, rst => rst, count_enable => '1', counter => cnt ); 

Simulação

Genéricos facilitam acelerar a simulação. Reduzindo cnt_bits e clk_cnt_len, podemos observar ciclos PWM completos em alguns microssegundos em vez de vários segundos.

Instanciação do testbench:

 DUT : entity work.pwm_led(str) generic map ( pwm_bits => 8, cnt_bits => 16, clk_cnt_len => 1 ); 

A forma de onda no ModelSim mostra um contador dente de serra controlando o brilho do LED. Um breve pulso "on" aparece na ponta de cada ciclo quando o ciclo de trabalho atinge seu valor máximo.

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Implementação em FPGA

Após síntese com iCEcube2, o projeto consome apenas 5% dos 64 LUTs disponíveis na peça ice40hx1ktq144:

 Resource Usage Report for pwm_led Mapping to part: ice40hx1ktq144 Cell usage: GND 3 uses SB_CARRY 31 uses SB_DFF 5 uses SB_DFFSR 39 uses SB_GB 1 use VCC 3 uses SB_LUT4 64 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%) Total load per clock: pwm_led|clk: 1 @S |Mapping Summary: Total LUTs: 64 (5%) 

Programar o FPGA via USB com o programador standalone Diamond foi simples. A animação LED resultante mostra uma rampa suave de brilho seguida por um breve escurecimento antes do ciclo repetir.

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

LED Respiratório com Ciclo de Trabalho de Onda Senoidal

Substituindo o contador dente de serra por uma tabela de lookup de onda senoidal produz um efeito "respiratório" natural, comumente visto em LEDs de notificação.

Implementando um Controlador PWM em VHDL: Projeto, Simulação e Demo em FPGA

Em um artigo futuro, mostraremos como construir um gerador de onda senoidal usando block RAM e integrá-lo ao projeto pwm_led.

Leia o próximo post: Como criar um efeito LED respiratório usando uma onda senoidal armazenada em block RAM

Veja também: Controlador de servo RC usando PWM de pino FPGA

VHDL

  1. Controlador de Potência PWM: Construa um Driver de Lâmpada com Modulação de Largura de Pulso
  2. Criando Listas de Strings em VHDL: Boas Práticas e Exemplo
  3. Criando um Testbench Dirigido por Tcl para um Módulo Code-Lock VHDL
  4. Finalizando Graciosamente uma Simulação de Testbench VHDL
  5. Gerando Números Aleatórios em VHDL: De Uniforme a OSVVM
  6. Projetando um FIFO Ring Buffer Robusto em VHDL com Block RAM
  7. Como Construir um Testbench de Auto-Verificação em VHDL – Guia Prático
  8. Implementando Lista Encadeada Dinâmica em VHDL com Tipos Protegidos e Ponteiros de Acesso
  9. Aproveitando Procedimentos In-Process para Design de FSM em VHDL Mais Limpo
  10. Guia Passo a Passo para Carregar Capacitores com Segurança e Eficiência