Hi:
I'm trying to control a servomotor HS-645MG with the Altera FPGA DE0
using VHDL. According to my limited knowledge (and according to other
programmers) these are the code for frequency dividing and pwm:
Frequency divider:
1 | library IEEE;
|
2 | use IEEE.STD_LOGIC_1164.ALL;
|
3 |
|
4 | entity clk64kHz is
|
5 | Port (
|
6 | clk : in STD_LOGIC;
|
7 | reset : in STD_LOGIC;
|
8 | clk_out: out STD_LOGIC
|
9 | );
|
10 | end clk64kHz;
|
11 |
|
12 | architecture Behavioral of clk64kHz is
|
13 | signal temporal: STD_LOGIC;
|
14 | signal counter : integer range 0 to 4999 := 0;
|
15 | begin
|
16 | freq_divider: process (reset, clk) begin
|
17 | if (reset = '1') then
|
18 | temporal <= '0';
|
19 | counter <= 0;
|
20 | elsif rising_edge(clk) then
|
21 | if (counter = 4999) then
|
22 | temporal <= NOT(temporal);
|
23 | counter <= 0;
|
24 | else
|
25 | counter <= counter + 1;
|
26 | end if;
|
27 | end if;
|
28 | end process;
|
29 |
|
30 | clk_out <= temporal;
|
31 | end Behavioral;
|
PWM
1 | library IEEE;
|
2 |
|
3 | use IEEE.STD_LOGIC_1164.all;
|
4 | use IEEE.STD_LOGIC_unsigned.all;
|
5 |
|
6 | -----------------------------------------------------
|
7 |
|
8 | entity pwm is
|
9 | port(
|
10 | clr : in std_logic;
|
11 | clk : in std_logic;
|
12 | duty : in std_logic_vector (7 downto 0);
|
13 | period : in std_logic_vector (7 downto 0);
|
14 | pwm : out std_logic
|
15 | );
|
16 |
|
17 | end pwm;
|
18 |
|
19 | -----------------------------------------------------
|
20 |
|
21 | architecture pwm of pwm is
|
22 |
|
23 | signal count : std_logic_vector(7 downto 0);
|
24 |
|
25 | begin
|
26 |
|
27 | cnt: process(clk, clr) -- 4 bit counter
|
28 | begin
|
29 | if clr = '1' then
|
30 | count <= "00000000";
|
31 | elsif clk'event and clk = '1' then
|
32 | if count = period -1 then
|
33 | count <= "00000000";
|
34 | else
|
35 | count <= count +1;
|
36 | end if;
|
37 | end if;
|
38 | end process cnt;
|
39 |
|
40 | pwmout: process(count, duty)
|
41 | begin
|
42 | if count < duty then
|
43 | pwm <= '1';
|
44 | else
|
45 | pwm <= '0';
|
46 | end if;
|
47 | end process pwmout;
|
48 |
|
49 | end pwm;
|
Top-design code
1 | -----------------------------------------------------
|
2 |
|
3 | library IEEE;
|
4 |
|
5 | use IEEE.STD_LOGIC_1164.all;
|
6 | use IEEE.STD_LOGIC_unsigned.all;
|
7 |
|
8 | -----------------------------------------------------
|
9 |
|
10 | entity pwm_top is
|
11 | port(
|
12 | clr : in std_logic;
|
13 | clk : in std_logic;
|
14 | duty : in std_logic_vector (7 downto 0);
|
15 | pwm : out std_logic
|
16 | );
|
17 |
|
18 | end pwm_top;
|
19 |
|
20 | -----------------------------------------------------
|
21 |
|
22 | architecture pwm_top of pwm_top is
|
23 |
|
24 | signal new_clock : std_logic;
|
25 |
|
26 | begin
|
27 |
|
28 | clk_div: entity work.clk64kHz
|
29 | port map(
|
30 | clk => clk, reset => '0', clk_out => new_clock);
|
31 |
|
32 | Pulse: entity work.pwm
|
33 | port map(
|
34 | clr => clr, clk => new_clock, duty => duty, period => "11001000", pwm => pwm);
|
35 |
|
36 | end pwm_top;
|
Described with my own words, the frequency is divided by a 5000 factor
which yields to a new frequency of 10000Hz (0.1ms). Then the period of
the pwm is assigned as 200 so the frequency of pwm will be 20ms and the
duty cycle will be controlled with the DE0 built-in switches.
Unfortunately this doesnt work very well when the servo is connected,
for example, if the duty cycle is 0.5ms the servo is supposed to moved
to one end but instead, it seems to take a 45º position. For every other
position, the servo also moves but it doesnt move accordingly to the
duty cycle.
I have tested the servo with an arduino uno and works wonders.
What could be the problem?
Any ideas are welcomed.