-
-
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; |
It has been a long time since I have looked at this code but here is my understanding.
What controls the width of the pulse?
The width depends on three factors:
PWM_clk
, the PWM clock frequency (is is based oni_CLK
divided down byprescale + 1
)quantas
, the total number of time quantas to accumulate (determines granularity of the PWM input over the 0-100% duty cycle range, and the total period of the PWM output signal)i_PWM
, the input PWM value
A faster PWM_clk
means PWM_acc
will increment more rapidly, resulting in the o_PWM
changing state more frequently and in turn results in shorter pulse widths.
quantas
specifies the max value the PWM_acc
is able to accumulate to, and is representative of the total period of one PWM cycle. So a larger value for quantas
means a longer PWM period but also more granularity.
i_PWM
is your input value that you are converting to PWM and is what directly controls the duty cycle and thus the width.
And what controls the frequency?
As hinted above, the output PWM signal’s frequency will be based on the input clock’s frequency (divided by the prescale + 1
) and the number of quantas
.
EDIT:
Dug up an old blog post I made on this which might provide additional insight:
http://carrierfrequency.blogspot.com/2013/11/here-is-yet-another-pwm-module-what.html
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?
@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.
What controls the width of the pulse? And what controls the frequency?