Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
12 years ago

pwm generation

Hi,

I would like to know is there any possibility to use always inside for loop??

please anybody explain the errors associated with my code both logical and coding syntax issues

ThankYOU

5 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    Hi,

    I would like to know is there any possibility to use always inside for loop??

    please anybody explain the errors associated with my code both logical and coding syntax issues

    ThankYOU

    --- Quote End ---

    I think you have been struggling for some time with your pwm work. I am afraid Your current coding is just wrong all over.

    Moreover your algorithm plan is not clear. I am here volunteering the below piece of code which is almost a precision pwm generator.

    and it can't be simpler(well it depends). The only drawback is that I have done it in matlab code and leave it to you to translate to hdl.

    it is just an accumultor and comparator. Don't worry at this stage about how it works.

    you can also avoid division /100 on the duty cycle by some simple bit shift trick (rescale duty cycle to divide by 128)

    
    clear all;
    n = 1000;   %this test length
    clk = 100;  %MHz system clock
    f1 = 1;     %MHz required output frequency
    %%%%%%%%%%%% accumulator %%%%%%%%%%%%%%%
    m = 1024;             %(assumed) lut size
    tw = floor(m*f1/clk); %tuning word for half cycle
    ptr = 0;              %pointer to lut
    for i = 1:n
        ptr = round(mod(ptr + tw, m));
        lut_data(i) = (ptr-1)*64;      %lut computed per pointer
    end
    %%%%%%%%%%%%% comparator %%%%%%%%%%%%%
    clkout = zeros(1,n);
    duty = 1;                          %1 ~ 100 percent
    threshold = 32768*duty*2/100;
    for i = 1:n
        if lut_data(i) <= threshold, clkout(i) = 32767; end;
    end
    %%%%%%%%%%%% check results %%%%%%%%%%%
    figure;plot(lut_data,'g-'); hold
    plot(clkout,'r.-');
    grid;
    d = diff(clkout);
    b = find(d);
    c = diff(b);
    duty_out = round(100*c(2)/(c(1)+c(2)))
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    here's a working PWM module

    
    --Author: some guy at digikey
    LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    USE ieee.std_logic_unsigned.all;
    ENTITY pwm IS
      GENERIC(
          sys_clk         : INTEGER := 50_000_000; --system clock frequency in Hz
          pwm_freq        : INTEGER := 100_000;    --PWM switching frequency in Hz
          bits_resolution : INTEGER := 8;          --bits of resolution setting the duty cycle
          phases          : INTEGER := 1);         --number of output pwms and phases
      PORT(
          clk       : IN  STD_LOGIC;                                    --system clock
          reset_n   : IN  STD_LOGIC;                                    --asynchronous reset
          ena       : IN  STD_LOGIC;                                    --latches in new duty cycle
          duty      : IN  STD_LOGIC_VECTOR(bits_resolution-1 DOWNTO 0); --duty cycle
          pwm_out   : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0);          --pwm outputs
          pwm_n_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0));         --pwm inverse outputs
    END pwm;
    ARCHITECTURE logic OF pwm IS
      CONSTANT period  : INTEGER := sys_clk/pwm_freq;                          --number of clocks in one pwm period
      TYPE counters IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period - 1; --data type for array of period counters
      SIGNAL count     : counters := (OTHERS => 0);                            --array of period counters
      SIGNAL half_duty : INTEGER RANGE 0 TO period/2 := 0;                     --number of clocks in 1/2 duty cycle
    BEGIN
      PROCESS(clk, reset_n)
      BEGIN
        IF(reset_n = '0') THEN                                             --asynchronous reset
          count <= (OTHERS => 0);                                            --clear counter
          pwm_out <= (OTHERS => '0');                                        --clear pwm outputs
          pwm_n_out <= (OTHERS => '0');                                      --clear pwm inverse outputs
        ELSIF(clk'EVENT AND clk = '1') THEN                                --rising system clock edge
          IF(ena = '1') THEN                                                 --latch in new duty cycle
            half_duty <= conv_integer(duty)*period/(2**bits_resolution)/2;     --determine clocks in 1/2 duty cycle
          END IF;
          FOR i IN 0 to phases-1 LOOP                                        --create a counter for each phase
            IF(count(0) = period - 1 - i*period/phases) THEN                   --end of period reached
              count(i) <= 0;                                                     --reset counter
            ELSE                                                               --end of period not reached
              count(i) <= count(i) + 1;                                          --increment counter
            END IF;
          END LOOP;
          FOR i IN 0 to phases-1 LOOP                                        --control outputs for each phase
            IF(count(i) = half_duty) THEN                                      --phase's falling edge reached
              pwm_out(i) <= '0';                                                 --deassert the pwm output
              pwm_n_out(i) <= '1';                                               --assert the pwm inverse output
            ELSIF(count(i) = period - half_duty) THEN                          --phase's rising edge reached
              pwm_out(i) <= '1';                                                 --assert the pwm output
              pwm_n_out(i) <= '0';                                               --deassert the pwm inverse output
            END IF;
          END LOOP;
        END IF;
      END PROCESS;
    END logic;
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    here's a working PWM module

    --- Quote End ---

    yes you can use a basic counter as such. Your design however expects a fixed freq generic but if freq is meant to be an input then you need to divide to get period cycles which can be avoided using accumulator.

    accumulator approach additionally supports fractional clk/freq and not just integer result.

    for a precision pwm (really precise) avoiding division is to use sine LUT + accumulator + comparator.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi,

    Thankyou

    inputting a frequency and duty cycle value , using counters concepts pwm generation is successful . as u said, to overcome the limitations of counters I am trying to learn accumulator techniques.

    But target or problem in my code is that... I need to generate a set of frequencies and their pwm from system clock .

    Like eg... 50mhz my system clock

    range of frequency 5mhz-30mhz with stepsize 5mhz.... ie, 5,10,15,20,25,30 like that. so I would like to add a loop to select the current frequency and then rest of program will be same.

    Thankyou
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    Hi,

    Thankyou

    inputting a frequency and duty cycle value , using counters concepts pwm generation is successful . as u said, to overcome the limitations of counters I am trying to learn accumulator techniques.

    But target or problem in my code is that... I need to generate a set of frequencies and their pwm from system clock .

    Like eg... 50mhz my system clock

    range of frequency 5mhz-30mhz with stepsize 5mhz.... ie, 5,10,15,20,25,30 like that. so I would like to add a loop to select the current frequency and then rest of program will be same.

    Thankyou

    --- Quote End ---

    with the simple counter technique you can only generate frequencies that differ in the ratio result of clk/freq and your resolution step is inherently limited to that.

    In the accumulator approach you can get much finer resolution (on average) since it is based on phase accumulation.

    In fact you don't need frequency step as input in any case.

    also I notice that the duty cycle ratio approach posted by mhouse1 doesn't make sense to me for example if duty cycle is 50% then he multiplies that by period say 50*10 then divides it by 2^8/2 ? where is the 100 then as 50 is ratio to 100. May be he assumes some rescaling at the 8 bit input (duty).

    Thus in both cases(counter or accum) you have two divisions to be dealt with(clk/freq and conversion of duty ratio).

    The accumulator bypasses the first division i.e. it implements it inherently but you need to precompute tuning word

    in both cases, the duty ratio is better converted from /100 ratio to /128 ratio