Chris_C

my 2nd ever vhdl PWM led

Recommended Posts

for my second project I decided to PWM the brightness of the LED here's what I ended up with...

 

is this basically the right idea?

library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity main is	Port (		clock : in STD_LOGIC;		led1 : out  STD_LOGIC	);end main;architecture Behavioral of main is	signal counter : STD_LOGIC_VECTOR(22 downto 0) := (others => '0');	signal onDuty : std_logic_vector(6 downto 0) := (others => '0');	signal dutyCount : std_logic_vector(6 downto 0) := (others => '0');	signal subCounter : std_logic_vector(10 downto 0) := "00000000001";begin	clock_proc: process(clock)		begin			if rising_edge(clock) then								counter(22 downto 0) <= counter(22 downto 0) + 1;								if counter = 0 then 										-- change the duty cycle					onDuty(6 downto 0) <= onDuty(6 downto 0) + 5; -- +5%										if onDuty > 100 then						onDuty <= (others => '0');					end if;									end if; -- counter = 0								-- down counter				subCounter(10 downto 0) <= subCounter(10 downto 0) - 1;								if subCounter = 0 then									subCounter <= "10000000000";					dutyCount(6 downto 0) <= dutyCount(6 downto 0) + 1;										if dutyCount > 100 then						dutyCount <= (others => '0');					end if;										if dutyCount < onDuty then						led1 <= '1';					else						led1 <= '0';					end if;									end if; -- subCounter = 0							end if; -- rising edge clock				end process;end Behavioral;

it does actually seem to work at any rate!

Share this post


Link to post
Share on other sites

If it works, then it is good.:-)

 

You could take a few shortcuts and save resources, you should be able to reduce resources by not havng "subcounter"  and instead using something like "if Counter(9 downto 0)  = 0 then..."

 

It is important to note that the order of the "if" and the counter increment is different to how it actually will happen - in this code it looks as though the increment happens first, then the "if". The "if" clause is evaluated with the value of the counter when the process is envoked, so it is the same as

 

                if counter = 0 then                    

                   ....
               end if; -- counter = 0

               counter(22 downto 0) <= counter(22 downto 0) + 1;
 
This gives a subtle bug in your code....
 
  onDuty(6 downto 0) <= onDuty(6 downto 0) + 5; -- +5%
 
  if onDuty > 100 then

    onDuty <= (others => '0');

  end if;

 

is the same as 
 
  if onDuty > 100 then

    onDuty <= (others => '0');

  else

    onDuty(6 downto 0) <= onDuty(6 downto 0) + 5; -- +5%

  end if;

 
This allows onDuty to become values > 100. What you might have really meant is
 
if onDuty > 100-5 then
  onDuty <= onDuty- 95; -- +5%, and wrap around - range is 0 to 100.
else
  onDuty <= onDuty + 5; -- +5%

end if;

 
Side issue - I always find it interesting how an 8 bit PWM counter can actually have 257 possible states - 0/256, 1/256.... 255/256, 256/256 - so needs nine bits to hold its desired state (or teh counter should should wrap at from 254 to 0, giving a different PWM frequency).

Share this post


Link to post
Share on other sites

okay so why did I change >99 to >100? doh !

nice spot!

 

binary duty cycle is slightly more efficient than percentage but with 7bits you're only "loosing" 28 values...

on a related matter the clock IP wizard can only go down to 1mhz is there no way to utilize the clock tiles for kHz type speeds?

Share this post


Link to post
Share on other sites

okay version two fades up then fades down and repeats....

library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;use ieee.numeric_std.all;entity main is	Port (		clock : in STD_LOGIC;		led1 : out  STD_LOGIC	);end main;architecture Behavioral of main is	signal counter : STD_LOGIC_VECTOR(21 downto 0) := (others => '0');	signal onDuty : signed(7 downto 0) := (others => '0');	signal dutyCount : signed(7 downto 0) := (others => '0');	signal subCounter : std_logic_vector(10 downto 0) := "00000000001";	signal direction : boolean := true;begin	clock_proc: process(clock)		begin			if rising_edge(clock) then								counter(21 downto 0) <= counter(21 downto 0) + 1;								if counter = 0 then 										-- change the duty cycle					if direction then						onDuty(7 downto 0) <= onDuty(7 downto 0) + 5; -- +5%					else 						onDuty(7 downto 0) <= onDuty(7 downto 0) - 5; -- -5%					end if;					if onDuty < 0 then						onDuty <= (others => '0');						direction <= not direction;					end if;					if onDuty > 99 then						onDuty <= "01100011";						direction <= not direction;					end if;				end if; -- counter = 0								-- down counter				subCounter(10 downto 0) <= subCounter(10 downto 0) - 1;								if subCounter = 0 then									subCounter <= "10000000000";					dutyCount(7 downto 0) <= dutyCount(7 downto 0) + 1;										if dutyCount > 99 then						dutyCount <= (others => '0');					end if;										if dutyCount < onDuty then						led1 <= '1';					else						led1 <= '0';					end if;									end if; -- subCounter = 0							end if; -- rising edge clock				end process;end Behavioral;

notice the extra sign bit(s) which allows you to detect downward direction going past zero to set the bounce back....

Share this post


Link to post
Share on other sites

Nope - the internal elements used to implement the clock synthisis have a fixed range they can work in. The "proper way" to run at 32kHz is to generate a "clock enable" pulse once every 1000 cycles, and run the whole design at 32MHz.

 

  if rising_edge(clk_32MHz) then

     if clock_enable = '1' then

     --- do stuff;  at 32kHz

    end if;

 

    if count = 999 then

      count <= (others => '0'); 

      clock_enable <= '1';

   else

     count <= count+ 1;

     clock_enable <= '0';

  end if;

 

However, you could try using the upper bit on a counter that is feed by a MHz range clock as a clock signal. You might want to feed the bit into a global clock buffer to make it work correctly....

 

The synthisis tools might complain about what you are doing though, as it migh foul up the static timing analysis.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now