实验原理
对于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实现呼吸灯的基本原理和实现方法