参考: FPGA活用回路&サンプル記述集(2) ―― モータやLEDを駆動するパワー回路|Tech Village (テックビレッジ) / CQ出版株式会社
- ちょうどnクロックで所望のpwm周期になるようなクロックを生成する。要するにソースクロックを(ソースクロック周波数/(pwm周波数*n))分周する。
- 分周器の出力を0 ~ (n-1)のn進カウンターでカウントする。
- デューティの指定値が0なら強制的にLを出力する。
- デューティの指定値がnなら強制的にHを出力する。
- n進カウンターの値が今0であったら次のカウントアップのタイミングでHを出力する。
- n進カウンターの値が今デューティの指定値(1~n)であったら次のカウントアップのタイミングでLを出力する。
- 反転出力もできるようにする。
VHDL_for_Quartus_Prime/pwm_gen at main · ti-nspire/VHDL_for_Quartus_Prime · GitHub
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; entity pwm_gen is generic( F_CLK : positive := 48_000_000; DUTY_MAX: positive := 100; PWM_FREQ: positive := 1000 ); port( aclr_n : in std_logic; clk : in std_logic; pol_inv: in std_logic; -- 0: そのまま出力; 1: 反転して出力 duty : in std_logic_vector(integer(ceil(log2(real(DUTY_MAX+1)))) - 1 downto 0); pwm_out: out std_logic ); end entity; architecture rtl of pwm_gen is constant DIV_FACTOR: positive := F_CLK/(PWM_FREQ * DUTY_MAX); signal clk_divided : std_logic := '1'; signal counter_mod : natural range 0 to DUTY_MAX - 1 := 0; signal pwm_out_inside: std_logic := '0'; begin -- クロック数DUTY_MAXで所望のpwm周期になるようソースクロック(F_CLK)を分周する。 process(clk) constant TOP_VAL: natural := DIV_FACTOR/2 - 1; variable count : natural range 0 to TOP_VAL := 0; begin if rising_edge(clk) then if count >= TOP_VAL then count := 0; clk_divided <= not clk_divided; else count := count + 1; end if; end if; end process; -- 分周したクロックをDUTY_MAX進カウンターでカウント(0 ~ (DUTY_MAX-1))する。 process(clk_divided, aclr_n) constant TOP_VAL: natural := DUTY_MAX - 1; begin if aclr_n = '0' then counter_mod <= 0; elsif rising_edge(clk_divided) then if counter_mod >= TOP_VAL then counter_mod <= 0; else counter_mod <= counter_mod + 1; end if; end if; end process; -- pwm波を生成する。 process(duty, clk_divided) begin -- 指定デューティが0ならLを出力し、 if unsigned(duty) = 0 then pwm_out_inside <= '0'; -- 指定デューティがDUTY_MAXならHを出力し、 elsif unsigned(duty) = DUTY_MAX then pwm_out_inside <= '1'; elsif rising_edge(clk_divided) then -- DUTY_MAX進カウンターがオーバーフローしていたら次のクロックでHを出力し、 if counter_mod = 0 then pwm_out_inside <= '1'; -- DUTY_MAX進カウンターが指定デューティに達していたら次のクロックでLを出力し、 elsif counter_mod = unsigned(duty) then pwm_out_inside <= '0'; end if; end if; end process; -- pwm波をそのまま出力または反転出力する。 pwm_out <= pwm_out_inside when pol_inv = '0' else not pwm_out_inside when pol_inv = '1' else '0'; end architecture;
テストベンチ:
LIBRARY ieee; USE ieee.std_logic_1164.all; use ieee.numeric_std.all; ENTITY pwm_gen_vhd_tst IS END pwm_gen_vhd_tst; ARCHITECTURE pwm_gen_arch OF pwm_gen_vhd_tst IS -- constants -- signals SIGNAL aclr_n : STD_LOGIC; SIGNAL clk : STD_LOGIC; SIGNAL duty : STD_LOGIC_VECTOR(6 DOWNTO 0); SIGNAL pol_inv : STD_LOGIC; SIGNAL pwm_out : STD_LOGIC; COMPONENT pwm_gen PORT ( aclr_n : IN STD_LOGIC; clk : IN STD_LOGIC; duty : IN STD_LOGIC_VECTOR(6 DOWNTO 0); pol_inv : IN STD_LOGIC; pwm_out : OUT STD_LOGIC ); END COMPONENT; BEGIN i1 : pwm_gen PORT MAP ( -- list connections between master ports and signals aclr_n => aclr_n, clk => clk, duty => duty, pol_inv => pol_inv, pwm_out => pwm_out ); process begin aclr_n <= '1'; pol_inv <= '0'; duty <= std_logic_vector(to_unsigned(20, duty'length)); wait; end process; process begin clk <= '1'; wait for 10417 ps; -- (1/F_CLK)/2 clk <= '0'; wait for 10417 ps; -- (1/F_CLK)/2 end process; END pwm_gen_arch;
シミュレーション結果。pwm周波数は1kHz、デューティは20%にした。