-
-
Save jjcarrier/7631350 to your computer and use it in GitHub Desktop.
-- Written By Jon Carrier | |
library IEEE; | |
use IEEE.STD_LOGIC_1164.all; | |
use IEEE.STD_LOGIC_UNSIGNED.ALL; | |
use ieee.numeric_std.all; | |
entity PWM is | |
generic ( | |
prescale: integer := 0; --frequency(PWM_clk)=frequency(i_CLK/(prescale+1)) | |
quantas: integer := 8 --PWM stages, determines granularity | |
); | |
port ( | |
i_CLK: in std_logic; --Main input clock signal | |
i_PWM: in std_logic_vector(31 downto 0); --Controls the Duty Cycle of o_PWM | |
o_PWM: out std_logic := '1' --The Pulse Width Modulated output signal | |
); | |
end PWM; | |
architecture PWM_arch of PWM is | |
signal PWM_clk : std_logic :='0'; | |
signal PWM_acc : std_logic_vector(31 downto 0) := x"00000000"; | |
begin | |
--PRESCALING PROCESS | |
--Generates the clock divided signal, frequency(PWM_clk)=frequency(i_CLK/(prescale+1)) | |
--PWM_clk drives the rest of the PWM logic | |
prescale_proc: process(i_CLK) | |
variable prescale_cnt : std_logic_vector(31 downto 0) := x"00000000"; | |
begin | |
if rising_edge(i_CLK) then | |
if prescale_cnt >= prescale then | |
prescale_cnt := x"00000000"; | |
PWM_clk <= not PWM_clk; | |
else | |
prescale_cnt := prescale_cnt + 1; | |
end if; | |
end if; | |
end process; | |
--ACCUMULATOR PROCESS | |
--Generates the accumlation variable PWM_acc | |
--This variable is used in the PWM as a means to determine | |
--when the PWM signal should switch. | |
accumulate: process(PWM_clk) | |
begin | |
if falling_edge(PWM_clk) then | |
if PWM_acc >= std_logic_vector(to_unsigned(quantas - 1, PWM_acc'length)) then | |
PWM_acc <= x"00000000"; | |
else | |
PWM_acc <= PWM_acc + 1; | |
end if; | |
end if; | |
end process; | |
--PWM PROCESS | |
--Generates the PWM output signal, o_PWM | |
--i_PWM controls the output and specifies how many Time Quantas | |
--out of a Total Quanta the output must be set HIGH | |
modulate: process(PWM_clk, i_PWM) | |
begin | |
if rising_edge(PWM_clk) then | |
if PWM_acc >= i_PWM then | |
o_PWM <= '0'; | |
elsif PWM_acc = x"00000000" then | |
o_PWM <= '1'; | |
end if; | |
end if; | |
end process; | |
end PWM_arch; |
@Eguy358, I will skip on writing the VHDL/Verilog code as I do not have a dev environment setup for this right now, but I can help with the math.
A 50Hz PWM will have a period of 20ms. You can break this period into fractions of time using time quantas
. To get a nice even 2ms pulse width you could use a value that is a multiple of 10. A higher value will give you better granularity if you need to provide a PWM output for a different pulse width or duty cycle. So we can go with quantas = 200
.
With this, we can now calculate the prescaler to make this work which follows the form of:
PWM frequency = Input Clock Frequency / (prescaler + 1) / quantas
50Hz = 12MHz / (prescaler + 1) / 200
This results in:
prescaler = 1199
Then with this configuration, each unit of i_PWM
is equal to:
i_PWM unit of time = (1/50)/200
or
i_PWM unit of time = 100us
So a 2ms pulse would be created using:
i_PWM = 2ms / 100us = 20
To summarize, to get a 2ms pulse width on a 50Hz PWM using a 12MHz clock, the following parameters could be used (there many possible solutions to this problem):
prescaler = 1199
(creates a PWM_clk of 10KHz)quantas = 200
(200 quantas for a 10KHz PWM_clk = 20ms or 50Hz)i_PWM = 20
Anyways hope that helps and I did not mix any math up.
Thank you a lot, i will give it a try :)
If you do have the time, no pressure, are you able to make an example where you generate a o_PWm of 50 Hz and width 2ms, from a 12MHz native clk?