my 2nd ever vhdl PWM led


Chris_C

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!

Link to comment
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).
Link to comment
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?

Link to comment
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....

Link to comment
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.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.