基于FPGA的呼吸灯的实现(vhdl实现)

实验原理


对于FPGA的引脚电压只有“0”和“1”两个电平,不能通过逐渐增加电压再逐渐减小电压实现要求,但是可以通过改变引脚单位时间内高电平的输出时间来实现呼吸灯,也就是让FPGA引脚输出一系列PWM波信号并不断改变PWM波的占空比实现呼吸灯的功能。

实现设计

  • pwm 输出模块
  • 占空比改变模块

实现代码

  • top文件
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity pwm_led is
 port(
  CLOCK_50    : in std_logic;      --50MHz
  KEY     : in std_logic_vector(3 downto 0); --low active
  LEDR    : out std_logic_vector(3 downto 0) --high-off, low-on
 );
end entity;

architecture rtl of pwm_led is
 constant CLK_FREQ  : integer  := 50 ;    --50MHz
 constant US_COUNT  : integer  := CLK_FREQ ;    --1 us counter
 constant MS_COUNT  : integer  := CLK_FREQ*1000 ;    --1 ms counter
 
 constant DUTY_STEP : unsigned(31 downto 0)  := 32D"100000" ;  --duty step
 constant DUTY_MIN_VALUE : unsigned(31 downto 0)  := 32UX"afffffff";--duty minimum value
 constant DUTY_MAX_VALUE : unsigned(31 downto 0)  := 32UX"ffffffff";--duty maximum value
 
 type state_type is (IDLE, PWM_PLUS, PWM_MINUS, PWM_GAP) ; 
 
 signal  state  : state_type;

 signal clk, rst_n  : std_logic;
 signal pwm_out   : std_logic;   --pwm output
 signal period   : unsigned(31 downto 0);    --pwm step value
 signal duty   : unsigned(31 downto 0);    --duty value
 signal pwm_flag  : std_logic;   --duty value plus and minus flag, 0: plus; 1:minus

 signal timer   : unsigned(31 downto 0);    --duty adjustment counter

 component pwm_core is
  port(
   clk   : IN  std_logic;
   rst   : IN  std_logic;
   period  : IN  unsigned(31 downto 0);  --pwm step value
   duty  : IN  unsigned(31 downto 0);  --duty value
   pwm_out  : OUT  std_logic    --pwm output
  );
 end component;

begin
 clk  <= CLOCK_50;
 rst_n <= KEY(0);
 LEDR(0) <=  pwm_out;
 LEDR(1) <=  pwm_out; 
 LEDR(2) <=  pwm_out; 
 LEDR(3) <=  pwm_out ; --leds low active

 process(clk, rst_n)
 begin
  if rst_n = '0' then
   period <= (others=>'0');
   timer <= (others=>'0');
   duty <= (others=>'0');
   pwm_flag <= '0' ;
   state <= IDLE;
  elsif rising_edge(clk) then
   case state is
    when IDLE =>
     period <= 32D"17179";    --The pwm step value, pwm 200Hz(period = 200*2^32/50000000)
     state <= PWM_PLUS;
     duty <= DUTY_MIN_VALUE;
     timer <= (others=>'0');
    when PWM_PLUS =>
     if duty > DUTY_MAX_VALUE - DUTY_STEP then  --if duty is bigger than DUTY MAX VALUE minus DUTY_STEP , begin to minus duty value
      pwm_flag <= '1' ;
      duty <= duty - DUTY_STEP ;
     else
      pwm_flag <= '0' ;
      duty <= duty + DUTY_STEP ;
     end if;
     state <= PWM_GAP ;
     timer <= (others=>'0');
    when PWM_MINUS =>
     if duty < DUTY_MIN_VALUE + DUTY_STEP then  --if duty is little than DUTY MIN VALUE plus duty step, begin to add duty value
      pwm_flag <= '0' ;
      duty <= duty + DUTY_STEP ;
     else
      pwm_flag <= '1' ;
      duty <= duty - DUTY_STEP ;
     end if;
     state <= PWM_GAP ;
     timer <= (others=>'0');
    when PWM_GAP =>
     if timer >= to_unsigned(MS_COUNT*200, 32) then     --adjustment gap is 100us
      if pwm_flag='1' then
       state <= PWM_MINUS;
      else
       state <= PWM_PLUS ;
      end if;
      timer <= (others=>'0');
     else
      timer <= timer + 1;
     end if;
    when others =>
     state <= IDLE;
   end case;
  end if;
 end process;
 
 --Instantiate pwm  
 pwm_m0 : pwm_core 
  port map(
  clk => clk,
  rst => not rst_n,
  period => period,
  duty => duty,
  pwm_out => pwm_out);
 
end rtl;

  • pwm输出
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity pwm_core is
 port(
  clk   : IN  std_logic;
  rst   : IN  std_logic;
  period  : IN  unsigned(31 downto 0);  --pwm step value
  duty  : IN  unsigned(31 downto 0);  --duty value
  pwm_out  : OUT  std_logic    --pwm output
 );
end entity;

architecture rtl of pwm_core is
 signal period_cnt  : unsigned(31 downto 0);
 signal  pwm_r  : std_logic;
begin

 pwm_out   <=  pwm_r;

 --period counter, step is period value
 process(clk, rst)
 begin
  if rst='1' then
   period_cnt <= (others=>'0');
  elsif rising_edge(clk) then
   period_cnt <= period_cnt + period;
  end if;
 end process;
 

 process(clk, rst)
 begin
  if rst='1' then
   pwm_r <= '0';
  elsif rising_edge(clk) then
   if period_cnt >= duty then  --if period counter is bigger or equals to duty value, then set pwm value to high
    pwm_r <= '1';
   else
    pwm_r <= '0';
   end if;
  end if;
 end process;

end rtl;

总结

实验很好的理解基于FPGA实现呼吸灯的基本原理和实现方法

上一篇:FPGA编程语言VHDL OR Verilog


下一篇:VHDL运算符和库的使用