Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

How to design a PWM wave generator with variable duty cycles?

Status
Not open for further replies.

ss_reddy23

Newbie level 6
Joined
Oct 1, 2020
Messages
11
Helped
1
Reputation
2
Reaction score
0
Trophy points
1
Activity points
121
In this the duty cycle needs to calculated and then be applied to pulse. Can anyone help me out
clk: input clock signal. The period of clk is Tc.
d: a 4-bit input signal. It is interpreted as an unsigned number and used to specify the
“off-interval” of the pulse signal.
w: a 4-bit input signal. It is interpreted as an unsigned number and used to specify the
“on-interval” of the pulse signal.
pulse: a 1-bit output.
 

Hi,

sounds like a school project.

What have you done so far?
Any paper and pencil drawings? (signal flow, timing diagram, schematic....)
Any ideas? What do you know already and where exactly do you need assistence?

What device (FPGA, ASIC, CPLD, microcontroller...)
What language
What IDE?

Klaus
 

What I'm thinking is to use the duty cycle and depending on that I can put output '1' or '0'.
like this

Rich (BB code):
cnt_duty_pr : process(clk, rst)
  begin
    if (rst = '1') then
      cnt_duty    <= (others => '0');
      pwm_out     <= '0';
    elsif (rising_edge(clk)) then       
      if (clk_en = '1') then
        cnt_duty <= cnt_duty + 1;
      end if;   
      if (cnt_duty < unsigned(duty)) then
    pwm_out <= '1';
      else
    pwm_out <= '0';

I'm writing it in VHDL . Whta I'm not understanding is how to calculate the duty cycle as we don't have any division operator in VHDL. How to make use of d and w to do so. I'm not dumping in any device just for simulation purpose with a TB .
 

Hi,

most probably you don´t need to divide. (At least I don´t see the question to do so)

if you output 2 pulses HIGH folowed by 6 pulses LOW then everything is done.
.. at least on the VHDL side.

if you are interested in, then you may calculate it...

******
you should draw what you have done so far. anyhow. Maybe as timing diagram (i do this on a checkered sheet) , but you may also do it like this:

lets say [ | ] is a rising clock edge:
then cnt_duty: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 ...

do you want this?

I can´t write VHDL but can read it a little. I assume there are a couple problems:
* cnt_duty is not defined
* pwm_out is not defined
* clk_en is not defined
* duty is not defined
* "end" is missing
maybe because it´s not finished yet...

...but you are on a good way.

Klaus
 

Hi,

most probably you don´t need to divide. (At least I don´t see the question to do so)

if you output 2 pulses HIGH folowed by 6 pulses LOW then everything is done.
.. at least on the VHDL side.

if you are interested in, then you may calculate it...

******
you should draw what you have done so far. anyhow. Maybe as timing diagram (i do this on a checkered sheet) , but you may also do it like this:

lets say [ | ] is a rising clock edge:
then cnt_duty: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 ...

do you want this?

I can´t write VHDL but can read it a little. I assume there are a couple problems:
* cnt_duty is not defined
* pwm_out is not defined
* clk_en is not defined
* duty is not defined
* "end" is missing
maybe because it´s not finished yet...

...but you are on a good way.

Klaus
Will try it and update!
 

Hi,

You will need a signal for your converter switching frequency, say fsw. Fsw will be different from clk and will be a very short duration pulse occurring at the beginning of every cycle of your switching frequency. You will use fsw to set pwm_out to '1'. Clk should be of very high frequency compared to few. You need another signal that which you might call "sawtooth". Sawtooth will be reset to '0' at the beginning of every fsw cycle and should be made to increase a definite amount every cycle of clk and should get to your desired PWM peak voltage value at the end of your converter switching period. That is to say that you scale the increment appropriately considering the number of few switching cycles that you have in one clk cycle. You need yet another signal, you can call it duty_in. Compare sawtooth with duty_in. When sawtooth is greater than or equal to duty_in, then pwm_out should be assigned '0', else pwm_out should be assigned '1'.

Derive fsw from clk using a counter. The number of bits that the counter has will determine how smooth the sawtooth will ramp up and it needs to be very smooth. So the higher the frequency of clk than that of fsw, the smoother the sawtooth will ramp up.

I hope this helps.
 
Last edited:

If this is supposed to be a fixed-frequency PWM
then your "on" and "off" times have to add up to
a constant value.

For things of this sort I have used 74AC series
binary counters with parallel load, one pair
that runs the full 8-bit count and one pair
that runs a down-count from the loaded value.
Full-count counter controls the "load" with
its carry and sets a SRFF. Down-counter resets
the SRFF at end of its cycle.

Maybe having a fixed frequency is not a
requirement but generally people want a
fixed frequency PWM, or they wayt fixed pulse
width PFM but hardly anyone wants -two-
variables.
 

Other responses seem to be going beyond the problem description that was given for the homework.

The problem clearly does not expect anything but setting the D and W values for off and on respectfully.

Simply a counter to count from 0 to d-1 with the output pulse set to 0 and count from 0 to w-1 with output pulse set to 1. For ease of understanding the logic this could be done with a simple two state FSM, with a single counter counter counting out 0 to d-1 and 0 to w-1 depending on which state the FSM is in.
--- Updated ---

I don't know if the problem description is intentionally misleading or was garbled by the OP, but based on the function of D and W that defines the duty cycle and pulse width without having to do calculate anything else.
 

Unfortunately, the OP has three variables (clk, d and w) instead of two [i.e. either clk and d, where we can find w as (\(2^n-1-d)\) or clk and w, where d is found to be (\(2^n-1-w)\), n being the number of bits of the counter].

It is worthy of note that if \((d+w)>2^n-1\) then that's not a constant frequency PWM, and that is what the OP seem to imply.

What I presented in Post #6 is a constant frequency PWM made up of a fine sawtooth waveform, an input signal to be compared with the sawtooth to set the instantaneous duty cycle and the comparator with output of '1' when the ON-time is not exceeded and '0' for the rest of the pwm_clk period after the ON-time is exceeded.

Like you said, the OP's question isn't worded to clearly specify either PWM or PFM.
 

clk isn't a variable, it's the clock input running the d and w counters.

I think the Tc clock period was misleading as it only sets the minimum resolution of the D and W counts for the width (in clocks) of the low and high values for the output pulse signal. Or maybe that was meant to be a generic for the code to set the minimum resolution of the pulses.

The statement about calculating the duty cycle was either something that was design to mislead (by who ever gave out the problem) or was something that was added by the OP thinking they needed it to solve the problem. The duty cycle is determined by the D and W values and nothing needs to be calculated.
 

We do not need D and W (along with clk) values to determine the duty cycle. If D is specified, then W is not needed and vice versa. We need either clk and D or clk and W. Whichever one of W and D that is provided, the other is inherently specified because \(w=2^n−1−d\) which also implies that \(d=2^n−1−w\), n being the number of bits of the counter. These expressions in Post #9 are completely mathematical. It doesn't mean that code would be written to determine D from W and clk or vice versa, but that with W alone D is inherent and vice versa.

The actual duty cycle in the OP's problem would be \(duty=\frac{w}{2^n−1}\). It can be argued that clk is not a variable but it can be seen that mathematically, \(\frac{w}{2^n-1}*clk_{period}=ON_{time}\).


The word variable in Post #9 should not be misunderstood as the declaration in VHDL as Post #9 is a mathematical analysis of the problem and does not imply implementation.

Post #6 implied some level of implementation and was a version to determine the duty cycle from an actual voltage signal coming in just like would be done in switching power supplies. The duty cycle in the OP's problem is completely based on time rather than on voltage and it is difficult for me to picture how much need it has.
 
Last edited:

I know about the calculations you are posting in #11, I'm purely going by what the OP posted in #1. As neither of us know what the instructor that assigned the problem actually gave as the question. I went with the assumption that the OP posted the question given to them.

If the OP mangled the question by making poor design decisions and posting them as the homework assignment, then perhaps what you've posted will help them. Otherwise if the requirements for the design are an input D and an input W for the low and high duration of a pulse then what I stated is perfectly valid.
 

I couldn't get the code working as I couldn't implement the duty cycle . I just took d(on value). Any help appreciated

Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity epwm is
  port (
    clk : in std_logic;
    reset : in std_logic;
     d,w: in std_logic_vector(3 downto 0);
    pulse : out std_logic
  );
end epwm;
 
architecture rtl_arch of epwm is 
--signal on1,off1:std_logic_vector(3 downto 0);
signal count:std_logic_vector(3 downto 0);
begin 
  
  process(clk,reset)
  
  begin
   if reset = '1' and clk = '1' then 
        pulse<='0';
    elsif d>count then 
        pulse<='1';
   else 
       pulse<='0';
   end if;
  end process;
  
  process(clk,reset)
  begin 
    if reset='1' and clk='1' then
       count<="0000";
     else
       count <= std_logic_vector(unsigned(count)+1); 
    end if;
  end process;
 
end rtl_arch;

 

Why if reset = '1' and clk = '1'...?
Are you trying to implement a synchronous reset? If so use:
Code:
if clk'event and clk='1' then
    if rst='1' then
        Count <= (others => '0');
        pulse <= '0';
    elsif...
        ...
    end if;
end if;

If async reset then use:
Code:
if rst='1' then
  count <= (others => '0');
  pulse <= '0';
elsif clk'event and clk='1' then
  ...
You might have to be careful with the reset signal though depending on what you are using your PWM for or you might blow something.

Notice that you declared d and w but didn't use w in the code. Why? Is it that it is not needed?
 
Last edited:

Why if reset = '1' and clk = '1'...?
Are you trying to implement a synchronous reset? If so use:
Code:
if clk'event and clk='1' then
    if rst='1' then
        Count <= (others => '0');
        pulse <= '0';
    elsif...
        ...
    end if;
end if;

If async reset then use:
Code:
if rst='1' then
  count <= (others => '0');
  pulse <= '0';
elsif clk'event and clk='1' then
  ...
You might have to be careful with the reset signal though depending on what you are using your PWM for or you might blow something.

Notice that you declared d and w but didn't use w in the code. Why? Is it that it is not needed?



Pulse is unknown . I know the if statements are failing. I don't know how to implement pwm

Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
 
entity epwm is
 
port (
  clk: in  std_logic;
  reset: in  std_logic;
  d: in  std_logic_vector(3 downto 0);  
  w:in  std_logic_vector(3 downto 0);  
  pulse: out std_logic);
end epwm;
 
architecture rtl_arch of epwm is
  signal count,count_on,count_off: unsigned(3 downto 0);
  signal p_n: std_logic;
begin
 
 
 process(d,w)
  begin 
    count_on<= unsigned(d);
     count_off<=unsigned(w);
  
  end process;
 process(clk,reset)
 begin
  if clk'event and clk='1' then
    if reset='1' then
        count <="0000";
        pulse <= '0';
          p_n <='0';
    elsif p_n = '0' then 
       
      if count = count_on -1 then 
             count<="0000";
                p_n <= '0';
                        pulse <= '0';
 
      else 
      count <= count+1;
        end if;
        
    elsif p_n = '0' then
        if count = count_off-1 then 
           count<="0000";
            p_n<='1';
                    pulse <= '1';
 
         else 
           count <= count+1;  
           
        end if;
  end if;
  end if;
 end process;
 
 
  
  
 
 
end rtl_arch;

 

We have asked why you have both D and W as input but you are ignoring us. You need help but you're ignoring the comments of the people you need help from.

Please look at preceding posts on this thread and comment on why you need D and W first.

Also tell us whether it is a homework or not, so we know how to help you.
 
Last edited:

As d
We have asked why you have both D and W as input but you are ignoring us. You need help but you're ignoring the comments of the people you need help from.

Please look at preceding posts on this thread and comment on why you need D and W first.

Also tell us whether it is a homework or not, so we know how to help you.
specifies the on duration and w off duration
 

    Akanimo

    Points: 2
    Helpful Answer Positive Rating
Please be consistent. Post #1 says W isfor ON interval and D for OFF interval.

We have explained to you that only one of D or W must be specified. You should specify that for the ON interval.

Or is it a homework?
--- Updated ---

Do not reset count to (others=>'0').
Reset to count to D. This is so that if it's used for SMPS the magnetics does not saturate. Resetting count to (others=>'0') would keep making pulse='1'. Resetting count to D means that you are resetting into the OFF interval, as pulse <= '0' when count >= D.
--- Updated ---

Do not reset pulse
--- Updated ---

The implementation would just be an up-counter that resets to D.
--- Updated ---

Outside the process, you'll have
Code:
pulse <= '1' when (count < D) else '0';
Use async reset.
 
Last edited:

If you want to define your generated pulse by independend on- and off-time parameters, the approach in post #15 is basically fine.

Some questions
What's the intended behaviour for D or W = 0? If you want no pulse, it has to be implemented differently. If you want a minimal on or off width of 1, you could omit the substract operation.

Why do you use std_logic_vector for numerical parameters?

By design of your code, the pulse period varies with both input parameters. That's not the usual way to implement a pwm generator, but it's nevertheless possible.
 

Hi,

I'm a friend of pictures.
Please post a simple timing diagram where we can see d and w and the expected timing.
No need to be perfect, hand drawn is O.K.

Klaus
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top