8 bit PWM modulator help

M

Mark Zerr

I am currently working on a 8 bit pulse width modulator circuit for a
graduate level Digital Design course. I have a couple problems with
my current code. I am using Altera's UP-1 education board with a Flex
10K CPLD. I am using Altera's Max Plus II Baseline educational
license for VHDL compiling and simulating. I am passing the PWM
output to all 8 segments of one of the LEDs on the board and using the
8 bit switch block for user control.

1. My counter seems to double count 1111 1111 and 0000 0000. This
does not really affect the operation, except that the counter causes
the overall PWM output's period to last 2 clock cycles longer than it
should.

2. Do I really need a clock divider process to be able to slow down
the PWM train to accurately be able to use the PWM output to vary the
LED brightness? Currently, I reduce the 25 Mhz clock down to about
123 Khz, which gives 240 PWM periods per second.

3. I have a glitch on the 1111 1111 control input condition caused by
my bad counter issue in #1 above. Not a big deal, but I like my stuff
to be perfect.


Here is my current code...any help or suggestions for improving it are
welcomed.

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.all;
USE IEEE.STD_LOGIC_ARITH.all;
USE IEEE.STD_LOGIC_UNSIGNED.all;

ENTITY PWM_8BIT is
PORT( clk, RESET : IN STD_LOGIC;
CONTROL : IN STD_LOGIC_VECTOR(7 downto 0);
COUNT_OUT : OUT STD_LOGIC_VECTOR(7 downto 0);
PWM_LED_OUT : OUT STD_LOGIC_VECTOR(7 downto 0);
CLK_DIV_OUT : OUT STD_LOGIC);
END PWM_8BIT;

ARCHITECTURE ARCHI of PWM_8BIT is
SIGNAL clk_div : STD_LOGIC;
SIGNAL COUNT_ALL : STD_LOGIC_VECTOR(7 downto 0);
SIGNAL CONTROL_INT : STD_LOGIC_VECTOR(7 downto 0);
SIGNAL PWM_INT : STD_LOGIC_VECTOR(7 downto 0);

BEGIN
CONTROL_INT <= CONTROL; --Store the User input in a signal

-- An up/down counter
PROCESS (clk_div, RESET)
VARIABLE UP_DOWN_INT : STD_LOGIC;
VARIABLE COUNT_INT : STD_LOGIC_VECTOR(7 downto 0);
VARIABLE DIRECTION : INTEGER;

BEGIN
IF RESET = '0' THEN --If RESET than count goes to "0"
COUNT_INT := "00000000";
ELSE
IF COUNT_INT = "00000000" THEN --Find the count direction
UP_DOWN_INT := '1';
END IF;
IF COUNT_INT = "11111111" THEN
UP_DOWN_INT := '0';
END IF;

IF UP_DOWN_INT = '1' THEN --Inc or dec selection
DIRECTION := 1;
ELSE
DIRECTION := -1;
END IF;

IF (clk_div'EVENT AND clk_div = '1') THEN --Update the counter on
the clock edge
COUNT_INT := COUNT_INT + DIRECTION;
END IF;
END IF;

COUNT_OUT <= COUNT_INT; --Copy the count to the output pins
COUNT_ALL <= COUNT_INT; --Pass the count to an internal signal
END PROCESS;

--Comparator Logic
PROCESS (clk_div, RESET)
BEGIN
IF RESET = '0' THEN --If reset then make pwm-->0
PWM_INT <= "11111111";
ELSE
IF COUNT_ALL >= CONTROL_INT THEN --Compare the count to the control
input value
PWM_INT <= "11111111"; --if bigger, then turn on the pwm
ELSE
PWM_INT <= "00000000"; --if smaller, then turn off the pwm
END IF;
END IF;
END PROCESS;

--Hack the PWM output if it is screwed up!
PROCESS (clk_div, RESET)
BEGIN
IF (clk_div'EVENT AND clk_div = '1') THEN --This makes sure the PWM
only changes on an edge
-- IF CONTROL_INT = "11111111" THEN --This stuff attempts to fix
"11111111" condition.
-- PWM_LED_OUT <= "00000000"; --This stuff attempts to fix
"11111111" condition.
-- ELSE --This stuff attempts to fix "11111111" condition.
PWM_LED_OUT <= PWM_INT;
-- END IF; --This stuff attempts to fix "11111111" condition.
END IF;
END PROCESS;

--Stupid clock divider
PROCESS(clk)
VARIABLE i : INTEGER range 0 to 100 := 0;
BEGIN
IF clk'event AND clk = '1' THEN
IF i = 100 THEN
i := 0;
clk_div <= NOT clk_div;
ELSE
i := i + 1;
END IF;
END IF;
CLK_DIV_OUT <= clk_div;
END PROCESS;

END ARCHI;
 
G

Garrett Mace

1. Is your up/down counter REALLY behaving the way you want it to? Is there
anything about your end-value comparisons that might keep your up/down
toggles from being activated in the current clock cycle?

2. Do you really even need an up/down counter, instead of a simple counter
that just rolls over to zero?

I haven't looked at the code very hard, but these might be two things to
consider.
 
M

Mark Zerr

Garrett Mace said:
1. Is your up/down counter REALLY behaving the way you want it to? Is there
anything about your end-value comparisons that might keep your up/down
toggles from being activated in the current clock cycle?

I suspect this is my problem, but I cannot figure out what is wrong
with my programming as it is currently coded.
2. Do you really even need an up/down counter, instead of a simple counter
that just rolls over to zero?

Yes, it must be an up/down counter....it is a requirement of the
project to simulate a triangular wave for the comparison to generate
the PWM train output.

I haven't looked at the code very hard, but these might be two things to
consider.


Thanks for your help though....any other ideas comments are
appreciated.
 
V

valentin tihomirov

Yes, it must be an up/down counter....it is a requirement of the
project to simulate a triangular wave for the comparison to generate
the PWM train output.
What are advantages of traingular over saw-edged wave? BTW, they both are
triangular.
 
M

Marcin

I am currently working on a 8 bit pulse width modulator circuit for a
graduate level Digital Design course. I have a couple problems with
my current code. I am using Altera's UP-1 education board with a Flex
10K CPLD. I am using Altera's Max Plus II Baseline educational
license for VHDL compiling and simulating. I am passing the PWM
output to all 8 segments of one of the LEDs on the board and using the
8 bit switch block for user control.

1. My counter seems to double count 1111 1111 and 0000 0000. This
does not really affect the operation, except that the counter causes
the overall PWM output's period to last 2 clock cycles longer than it
should.

2. Do I really need a clock divider process to be able to slow down
the PWM train to accurately be able to use the PWM output to vary the
LED brightness? Currently, I reduce the 25 Mhz clock down to about
123 Khz, which gives 240 PWM periods per second.

3. I have a glitch on the 1111 1111 control input condition caused by
my bad counter issue in #1 above. Not a big deal, but I like my stuff
to be perfect.


Here is my current code...any help or suggestions for improving it are
welcomed.

Mark,

Look carefully at the sensitivity list - comparator logic is simple
combinational logic. I will change it to: PROCESS (COUNT_ALL,
CONTROL_INT, RESET)

[...]
--Comparator Logic
PROCESS (clk_div, RESET)
BEGIN
IF RESET = '0' THEN --If reset then make pwm-->0
PWM_INT <= "11111111";
ELSE
IF COUNT_ALL >= CONTROL_INT THEN --Compare the count to the control
input value
PWM_INT <= "11111111"; --if bigger, then turn on the pwm
ELSE
PWM_INT <= "00000000"; --if smaller, then turn off the pwm
END IF;
END IF;
END PROCESS;
[...]

Stupid clock divider is really stupid without reset;)
--Stupid clock divider
PROCESS(clk)
VARIABLE i : INTEGER range 0 to 100 := 0;
BEGIN
IF clk'event AND clk = '1' THEN
IF i = 100 THEN
i := 0;
clk_div <= NOT clk_div;
ELSE
i := i + 1;
END IF;
END IF;
CLK_DIV_OUT <= clk_div;
END PROCESS;

END ARCHI;

I hope this help a little.

Marcin
 
M

Mark Zerr

valentin tihomirov said:
What are advantages of traingular over saw-edged wave? BTW, they both are
triangular.

To be honest....I would have not even wanted to use an up/down
counter. I would have simply counted from 0 to max...with the middle
being the the pseudo cross over point for the "triangular" wave. For
some reason my professor wanted a true up/down counter. I think it
was to show the problems that can arise when designing a counter like
this. As for the saw toothed wave, I am not sure what the advantage
of that wave would be compared to the way I am doing it now. In all
reality it does not matter. The circuit works perfectly fine right
now, but I was curious if I was missing some good design principles,
since I am so new to vhdl.
 
M

Mark Zerr

Mark,

Look carefully at the sensitivity list - comparator logic is simple
combinational logic. I will change it to: PROCESS (COUNT_ALL,
CONTROL_INT, RESET)

Yes this is a good point. I have changed the sensitivity list.
Sometimes I still get confused about the proper sensitivities for a
given process. VHDL is very foreign to me since everything is
happening concurrently. This type of programming caused me major
headaches when I first started learning a couple weeks ago.
Stupid clock divider is really stupid without reset;)


I hope this help a little.

Marcin

I agree that my clock divider should have a reset. This has been
fixed. It was an oversight...trust me!

Thanks for your input Marcin!
 
G

Garrett Mace

To be honest....I would have not even wanted to use an up/down
counter. I would have simply counted from 0 to max...with the middle
being the the pseudo cross over point for the "triangular" wave. For
some reason my professor wanted a true up/down counter. I think it
was to show the problems that can arise when designing a counter like
this. As for the saw toothed wave, I am not sure what the advantage
of that wave would be compared to the way I am doing it now. In all
reality it does not matter. The circuit works perfectly fine right
now, but I was curious if I was missing some good design principles,
since I am so new to vhdl.


A saw-toothed "wave" is simpler to understand and implement. Part of a
project I did a while back was generating eight different sine waves through
PWM. I used one very simple PWM clock that looked like this:

***
entity PWMClock is
port (
CLK, Enable, RST : in std_logic;
PWMClockOut : out std_logic_vector(7 downto 0)
);
end PWMClock;

architecture PWM of PWMClock is

signal PWMTemp : std_logic_vector(7 downto 0);

begin

process (CLK, RST)
begin
if (RST = '1') then
PWMTemp <= "00000000";
elsif (rising_edge(CLK) and Enable = '1') then
PWMTemp <= PWMTemp + 1;
end if;
end process;

PWMClockOut <= PWMTemp;

end PWM;
***



This was a "sawtooth wave," merely a counter that rolls over. Then the eight
PWM modules all used this same clock. The actual PWM output modules were
also very simple:



***
entity PWMOutput is
port (
RST : in std_logic;
PWMLevel, PWMClock : in std_logic_vector(7 downto 0);
PWMOut : out std_logic
);
end PWMOutput;

architecture PWM of PWMOutput is

signal PWMOutTemp : std_logic;

begin

process (PWMClock, RST) is

begin

if (PWMClock = "00000000") then
PWMOutTemp <= '1';
elsif (PWMClock = PWMLevel or RST = '1') then
PWMOutTemp <= '0';
end if;

end process;

PWMOut <= PWMOutTemp;

end PWM;
***

PWMLevel was piped in from a sine table. As you can see, the PWM module just
checked for equality and then toggled a flip-flop, to turn off the output
once the PWM value had been reached. This was Xilinx VHDL instead of Altera,
so some parts might be less familiar to you. There may be a more optimized
solution for PWM if you work in structural instead of behavioral, but this
is pretty simple.
 
M

Mark Zerr

Garrett Mace said:
A saw-toothed "wave" is simpler to understand and implement. Part of a
project I did a while back was generating eight different sine waves through
PWM. I used one very simple PWM clock that looked like this:

***
entity PWMClock is
port (
CLK, Enable, RST : in std_logic;
PWMClockOut : out std_logic_vector(7 downto 0)
);
end PWMClock;

architecture PWM of PWMClock is

signal PWMTemp : std_logic_vector(7 downto 0);

begin

process (CLK, RST)
begin
if (RST = '1') then
PWMTemp <= "00000000";
elsif (rising_edge(CLK) and Enable = '1') then
PWMTemp <= PWMTemp + 1;
end if;
end process;

PWMClockOut <= PWMTemp;

end PWM;
***



This was a "sawtooth wave," merely a counter that rolls over. Then the eight
PWM modules all used this same clock. The actual PWM output modules were
also very simple:

OK...this makes sense.
***
entity PWMOutput is
port (
RST : in std_logic;
PWMLevel, PWMClock : in std_logic_vector(7 downto 0);
PWMOut : out std_logic
);
end PWMOutput;

architecture PWM of PWMOutput is

signal PWMOutTemp : std_logic;

begin

process (PWMClock, RST) is

begin

if (PWMClock = "00000000") then
PWMOutTemp <= '1';
elsif (PWMClock = PWMLevel or RST = '1') then
PWMOutTemp <= '0';
end if;

end process;

PWMOut <= PWMOutTemp;

end PWM;
***

PWMLevel was piped in from a sine table. As you can see, the PWM module just
checked for equality and then toggled a flip-flop, to turn off the output
once the PWM value had been reached. This was Xilinx VHDL instead of Altera,
so some parts might be less familiar to you. There may be a more optimized
solution for PWM if you work in structural instead of behavioral, but this
is pretty simple.

Yeah...I see where you are going now...this is a nice method. I have
decided that my current glitch is not that big of deal though, and I
intend to just leave it the way I am currently doing it. Thanks for
all the input though...it was interesting learning another way to
accomplish this circuit.
 
B

Brian Drummond

To be honest....I would have not even wanted to use an up/down
counter. I would have simply counted from 0 to max...with the middle
being the the pseudo cross over point for the "triangular" wave. For
some reason my professor wanted a true up/down counter.

Single-sided PWM (sawtooth) and double-sided PWM (triangle) have
different distortion characteristics, double sided having lower
distortion because it isn't also a form of PPM (pulse position
modulation). Perhaps this is what your professor is looking for?

- Brian
 
M

Mark Zerr

Brian Drummond said:
Single-sided PWM (sawtooth) and double-sided PWM (triangle) have
different distortion characteristics, double sided having lower
distortion because it isn't also a form of PPM (pulse position
modulation). Perhaps this is what your professor is looking for?

- Brian

Brian,

This is interesting...I had not considered this. I spoke with my
professor, and he seemed to indicate I was making this far more
difficult than necessary. He said, he really just wanted to be sure
we could work with vhdl and create a simple circuit. I think he also
wanted us to create a PWM that basically was equivalent to its analog
triangular analog brother.

Mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top