使用VHDL进行脉冲宽度调制

时间:2015-04-14 00:34:58

标签: vhdl pwm

我正在尝试创建一个使用100khz时钟和PWM范围从.6ms到2.4ms的PWM发生器但是我坚持将其实现为vhdl我一直在尝试使用状态机来执行此操作但它具有变得比它应该更复杂。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity PWM is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           pwm_out : out  STD_LOGIC;
              PWM_CONST : STD_LOGIC_VECTOR(2 downto 0));
end PWM;

architecture Behavioral of PWM is
type State_type is (LOW,HIGH,Counting); --States
signal Sreg, Snext : State_type; --Curent state and next state
signal t20ms,t100ms,t_6ms,t_825ms,t1_05ms,t1_275ms,t1_5ms,t1_725ms,t1_95ms,t2_175ms,t2_4ms: STD_LOGIC; 
signal twenty : STD_LOGIC;
signal count_flag : STD_LOGIC; 
begin
-------------------------Clock Dividers for timing---------------------------------------
process(clk,reset)
variable toggle_20ms,toggle_1666hz,toggle_1212hz,toggle_952hz,toggle_785hz,toggle_666hz,toggle_579hz,toggle_512hz,toggle_460hz,toggle_416hz  : STD_LOGIC :='0'; 
variable counter_20ms : integer range 0 to 999; 
variable counter_1666hz : integer range 0 to 29;
variable counter_1212hz : integer range 0 to 40;
variable counter_952hz : integer range 0 to 51;
variable counter_785hz : integer range 0 to 63;
variable counter_666hz : integer range 0 to 74;
variable counter_579hz : integer range 0 to 85;
variable counter_512hz : integer range 0 to 96;
variable counter_460hz : integer range 0 to 107;
variable counter_416hz : integer range 0 to 119;
 begin
 if reset = '1' then
      t_6ms<='0';
        t_825ms<='0';
        t1_05ms<='0';
        t1_275ms<='0';
        t1_5ms<='0';
        t1_725ms<='0';
        t1_95ms<='0';
        t2_175ms<='0';
        t2_4ms<='0';
        twenty<='0';
 else
    if(clk'event and clk = '1') then 
        counter_20ms := counter_20ms+1;
        counter_1666hz := counter_1666hz+1;
        counter_1212hz := counter_1212hz+1;
        counter_952hz := counter_952hz+1;
        counter_785hz := counter_785hz+1;
        counter_666hz := counter_666hz+1;
        counter_579hz := counter_579hz+1;
        counter_512hz := counter_512hz+1;
        counter_460hz := counter_460hz+1;
        counter_416hz := counter_416hz+1; 
        counter_20ms := counter_20ms+1; 
        if (counter_1666hz = 29) then 
            toggle_1666hz := not toggle_1666hz; 
            counter_1666hz := 0; 
        end if;
        if (counter_1212hz = 40) then 
            toggle_1212hz := not toggle_1212hz; 
            counter_1212hz := 0; 
        end if;
        if (counter_952hz  = 51) then 
            toggle_952hz  := not toggle_952hz ; 
            counter_952hz  := 0; 
        end if;
        if (counter_785hz = 63) then 
            toggle_785hz := not toggle_785hz; 
            counter_785hz := 0; 
        end if;
        if (counter_666hz = 74) then 
            toggle_666hz := not toggle_666hz; 
            counter_666hz := 0; 
        end if;
        if (counter_579hz = 85) then 
            toggle_579hz := not toggle_579hz; 
            counter_579hz := 0; 
        end if;
        if (counter_512hz = 96) then 
            toggle_512hz := not toggle_512hz; 
            counter_512hz := 0; 
        end if;
        if (counter_460hz = 107) then 
            toggle_460hz := not toggle_460hz; 
            counter_460hz := 0; 
        end if;
        if (counter_416hz = 119) then 
            toggle_416hz := not toggle_416hz; 
          counter_416hz := 0; 
       end if;
        if (counter_20ms = 999) then 
            toggle_20ms := not toggle_20ms;
            counter_20ms := 0;
        end if; 
        t_6ms<=toggle_1666hz;
        t_825ms<=toggle_1212hz;
        t1_05ms<=toggle_952hz;
        t1_275ms<=toggle_785hz;
        t1_5ms<=toggle_666hz;
        t1_725ms<=toggle_579hz;
        t1_95ms<=toggle_512hz;
        t2_175ms<=toggle_460hz;
        t2_4ms<=toggle_416hz;
        twenty<=toggle_20ms;
    end if; 
end if;
end process; 

-------------------------Next State Logic----------------------------------
    process(Sreg,reset,PWM_CONST)
        begin 
            case Sreg is 
                when LOW => if (rising_edge(twenty)) then Snext <= HIGH; 
                                else                                 Snext <= LOW; 
                                end if; 
                when HIGH => if reset = '1'        then Snext <= LOW;
                                 else                   Snext <= Counting; 
                                 end if;
                when Counting => if count_flag = '1'     then Snext <= Counting;
                                      else               Snext <= LOW; 
                                      end if; 
            end case; 
    end process; 
-----------------------Update State----------------------------------------
    process(clk)
        begin 
            if (clk'event and clk='1') then 
                Sreg <= Snext;
            end if;
    end process; 
------------------------Count_flag---------------------------------------------
    process(clk,PWM_CONST)
        begin
        case PWM_CONST is
                when "000"=> t_6ms <= count_flag;
                when "001"=> t_825ms <= count_flag;
                when "010"=> t1_05ms <= count_flag;
                when "011"=> t1_275ms <= count_flag;
                when "100"=> t1_5ms <= count_flag;
                when "101"=> t1_725ms <= count_flag;
                when "110"=> t1_95ms <= count_flag;
                when "111"=> t2_175ms <= count_flag;
                when others => t2_4ms <= count_flag;
        end case;       
    end process; 
----------------------Output Logic-----------------------------------------
with Sreg select -- output logic based on state only
        pwm_out <= '1' when HIGH | Counting,
                      '0' when LOW,
                      '0' when others;
---------------------------------------------------------------------------                   
end Behavioral;
<code>

当我尝试合成

时出现错误的同步错误

1 个答案:

答案 0 :(得分:1)

您的代码至少存在三个主要问题。第一个是你的下一个州过程:

process(Sreg,reset,PWM_CONST)
    begin 
        case Sreg is 
            when LOW => if (rising_edge(twenty)) then Snext <= HIGH; 
                            else                                 Snext <= LOW; 
                            end if; 
            when HIGH => if reset = '1'        then Snext <= LOW;
                             else                   Snext <= Counting; 
                             end if;
            when Counting => if count_flag = '1'     then Snext <= Counting;
                                  else               Snext <= LOW; 
                                  end if; 
        end case; 
end process;

在案件中你不能拥有rising_edge,我不确定它代表什么样的电子元件。使用rising_edge时,所有语句都必须包含在其中(异步复位除外),就像你的时钟分频器和更新状态进程一样。

如果您想检测信号twenty来自&#39; 0&#39;到&#39; 1&#39;,在这里你将如何做到这一点:

process(reset, clk)
begin
    if reset = '1' then
        twenty_dl <= '0';
    elsif rising_edge(clk) then
        twenty_dl <= twenty; -- Delayed version of twenty

        if twenty_dl = '0' and twenty = '1' then -- twenty is rising
            -- Insert logic here
        end if;
    end if;
end process;

作为旁注,由寄存器驱动的信号绝不能用作时钟。时钟是合理的,它们来自fpga上的时钟使能引脚,并通过PLL等进行修改。支持使用寄存器来驱动时钟信号(不幸的是),但该工具不能像对全局时钟那样分析网络,即如果没有工具告诉您,您的设计可能会失败。如果你想&#34;划分&#34;时钟,使用时钟启用:

process(reset, clk)
begin
    if reset = '1' then
        cnt <= (others => '0');
    elsif rising_edge(clk) then
        if cnt = 199 then -- Divide the clock by 200
            clk_div_200_en <= '1';
            cnt <= (others => '0');
        else
            clk_div_200_en <= '0';
            cnt <= cnt + 1;
        end if;
    end if;
end process;

process(clk)
begin
    if rising_edge(clk) and clk_div_200_en = '1' then
        -- Insert logic here
    end if;
end process;

您的代码的第二个主要问题是您有多个驱动程序用于信号t_6ms和cie。时钟分频器进程和count_flag进程都会分配这些信号。在VHDL中,信号的所有分配必须在单个过程中完成。否则,您将获得多个驱动程序,这些驱动程序通常在综合中不受支持,并且可能导致网络冲突。

第三个主要问题是count_flag进程。这个过程是组合的(没有时钟)。在组合过程中,过程分配的任何信号必须在整个过程的每条路径中分配。这意味着,t_6msPWM_CONST时必须分配"001", "010", etc。如果不这样做会导致闩锁元素,这很糟糕!