Last active
March 8, 2022 22:34
-
-
Save jjcarrier/7631350 to your computer and use it in GitHub Desktop.
A parameterized PWM module written in VHDL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@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 withquantas = 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.