65.9K
CodeProject 正在变化。 阅读更多。
Home

PWM 和 VHDL 实现舵机控制

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (10投票s)

2012年12月20日

LGPL3

2分钟阅读

viewsIcon

131176

downloadIcon

1715

使用 VHDL 生成 PWM 信号以控制舵机。

Simulation of the PWM for servomotor

引言

在这篇简短的文章中,我们将使用 VHDL 生成用于舵机控制的脉宽调制 (PWM) 信号。

背景

首先,舵机不过是带有附加电子电路的直流电机,目的是实现更好的控制。为了进行这种控制,我们需要生成如下波形

PWM signal for servomotor control

脉冲的时间或频率决定了舵机的角度位置。每个舵机都有自己的频率范围,由制造商在数据手册中提供。图示的值在 1ms 到 2ms 之间。

控制设计

舵机的控制信号由两个频率组成

  • 刷新频率,为 20ms。
  • 脉冲宽度范围,由制造商提供。对于本文,我们假设该频率范围为 0.5ms 到 2.5ms。

首先,我们需要确定我们的工作范围,由下式给出

Range of operation

现在我们需要知道舵机的分辨率,即它可以采取的位置数量。因此,所需的最小频率相当于

Minimum needed frequency

如果我们的舵机最多可以采取 128 个位置,那么结果是

Minimum needed frequency, substitution

现在我们需要一个 64kHz 的频率分频器。虽然我们可以按照 VHDL 实现频率分频器 中描述的过程来做,但这里我们提供了从 50MHz 到 64kHz 的完整分频器代码

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
 
entity clk64kHz is
    Port (
        clk    : in  STD_LOGIC;
        reset  : in  STD_LOGIC;
        clk_out: out STD_LOGIC
    );
end clk64kHz;
 
architecture Behavioral of clk64kHz is
    signal temporal: STD_LOGIC;
    signal counter : integer range 0 to 780 := 0;
begin
    freq_divider: process (reset, clk) begin
        if (reset = '1') then
            temporal <= '0';
            counter  <= 0;
        elsif rising_edge(clk) then
            if (counter = 780) then
                temporal <= NOT(temporal);
                counter  <= 0;
            else
                counter <= counter + 1;
            end if;
        end if;
    end process;
 
    clk_out <= temporal;
end Behavioral;

最后,我们知道使用 64kHz 时钟,每 64 次迭代将有 1ms。因此,为了获得 20ms 的频率,我们只需要将 20 * 64 相乘,这将使用一个从 0 到 1279 的计数器来实现。

VHDL 实现

对于 VHDL 实现,我们有三个输入:64 kHz 时钟、复位和可以取 0 到 127 值的向量。唯一的输出是舵机控制信号。代码如下所示。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity servo_pwm is
    PORT (
        clk   : IN  STD_LOGIC;
        reset : IN  STD_LOGIC;
        pos   : IN  STD_LOGIC_VECTOR(6 downto 0);
        servo : OUT STD_LOGIC
    );
end servo_pwm;

architecture Behavioral of servo_pwm is
    -- Counter, from 0 to 1279.
    signal cnt : unsigned(10 downto 0);
    -- Temporal signal used to generate the PWM pulse.
    signal pwmi: unsigned(7 downto 0);
begin
    -- Minimum value should be 0.5ms.
    pwmi <= unsigned('0' & pos) + 32;
    -- Counter process, from 0 to 1279.
    counter: process (reset, clk) begin
        if (reset = '1') then
            cnt <= (others => '0');
        elsif rising_edge(clk) then
            if (cnt = 1279) then
                cnt <= (others => '0');
            else
                cnt <= cnt + 1;
            end if;
        end if;
    end process;
    -- Output signal for the servomotor.
    servo <= '1' when (cnt < pwmi) else '0';
end Behavioral;

信号 cnt 用于实现从 0 到 1279 的计数器,其描述在第 22 行到第 33 行。

输入信号 pos 是一个可以取 0 到 127 之间的任何值的向量,产生 0ms2ms 的范围。由于我们需要一个从 0.5ms2.5ms 的信号,我们需要添加一个相当于 0.5ms 的偏移量 32。

列表 3,如下所示,描述了将频率分频器和舵机控制组件连接在一起所需的 PORT MAP

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity servo_pwm_clk64kHz is
    PORT(
        clk  : IN  STD_LOGIC;
        reset: IN  STD_LOGIC;
        pos  : IN  STD_LOGIC_VECTOR(6 downto 0);
        servo: OUT STD_LOGIC
    );
end servo_pwm_clk64kHz;

architecture Behavioral of servo_pwm_clk64kHz is
    COMPONENT clk64kHz
        PORT(
            clk    : in  STD_LOGIC;
            reset  : in  STD_LOGIC;
            clk_out: out STD_LOGIC
        );
    END COMPONENT;
    
    COMPONENT servo_pwm
        PORT (
            clk   : IN  STD_LOGIC;
            reset : IN  STD_LOGIC;
            pos   : IN  STD_LOGIC_VECTOR(6 downto 0);
            servo : OUT STD_LOGIC
        );
    END COMPONENT;
    
    signal clk_out : STD_LOGIC := '0';
begin
    clk64kHz_map: clk64kHz PORT MAP(
        clk, reset, clk_out
    );
    
    servo_pwm_map: servo_pwm PORT MAP(
        clk_out, reset, pos, servo
    );
end Behavioral;

测试平台和仿真

对于测试平台,我们使用 pos 的五个不同值,描述在第 43 行到第 59 行。

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
 
ENTITY servo_pwm_clk64kHz_tb IS
END servo_pwm_clk64kHz_tb;
 
ARCHITECTURE behavior OF servo_pwm_clk64kHz_tb IS
    -- Unit under test.
    COMPONENT servo_pwm_clk64kHz
        PORT(
            clk   : IN  std_logic;
            reset : IN  std_logic;
            pos   : IN  std_logic_vector(6 downto 0);
            servo : OUT std_logic
        );
    END COMPONENT;

    -- Inputs.
    signal clk  : std_logic := '0';
    signal reset: std_logic := '0';
    signal pos  : std_logic_vector(6 downto 0) := (others => '0');
    -- Outputs.
    signal servo : std_logic;
    -- Clock definition.
    constant clk_period : time := 10 ns;
BEGIN
    -- Instance of the unit under test.
    uut: servo_pwm_clk64kHz PORT MAP (
        clk => clk,
        reset => reset,
        pos => pos,
        servo => servo
    );

   -- Definition of the clock process.
   clk_process :process begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;
 
    -- Stimuli process.
    stimuli: process begin
        reset <= '1';
        wait for 50 ns;
        reset <= '0';
        wait for 50 ns;
        pos <= "0000000";
        wait for 20 ms;
        pos <= "0101000";
        wait for 20 ms;
        pos <= "1010000";
        wait for 20 ms;
        pos <= "1111000";
        wait for 20 ms;
        pos <= "1111111";
        wait;
    end process;
END;

仿真结果如下所示。获得了以下频率

  • 刷新频率为 19.9936 ms
  • 最小频率为 0.4920 ms
  • 最大频率为 2.4835 ms

Simulation of the PWM for servomotor

© . All rights reserved.