Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)

Servomotor. PWM and VHDL

Altera_Forum
Honored Contributor II
4,359 Views

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: 

 

 

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 4999 := 0; begin freq_divider: process (reset, clk) begin if (reset = '1') then temporal <= '0'; counter <= 0; elsif rising_edge(clk) then if (counter = 4999) then temporal <= NOT(temporal); counter <= 0; else counter <= counter + 1; end if; end if; end process; clk_out <= temporal; end Behavioral; 

 

 

PWM 

library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_unsigned.all; ----------------------------------------------------- entity pwm is port( clr : in std_logic; clk : in std_logic; duty : in std_logic_vector (7 downto 0); period : in std_logic_vector (7 downto 0); pwm : out std_logic ); end pwm; ----------------------------------------------------- architecture pwm of pwm is signal count : std_logic_vector(7 downto 0); begin cnt: process(clk, clr) -- 4 bit counter begin if clr = '1' then count <= "00000000"; elsif clk'event and clk = '1' then if count = period -1 then count <= "00000000"; else count <= count +1; end if; end if; end process cnt; pwmout: process(count, duty) begin if count < duty then pwm <= '1'; else pwm <= '0'; end if; end process pwmout; end pwm; 

 

 

Top-design code 

----------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_unsigned.all; ----------------------------------------------------- entity pwm_top is port( clr : in std_logic; clk : in std_logic; duty : in std_logic_vector (7 downto 0); pwm : out std_logic ); end pwm_top; ----------------------------------------------------- architecture pwm_top of pwm_top is signal new_clock : std_logic; begin clk_div: entity work.clk64kHz port map( clk => clk, reset => '0', clk_out => new_clock); Pulse: entity work.pwm port map( clr => clr, clk => new_clock, duty => duty, period => "11001000", pwm => pwm); 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.
0 Kudos
5 Replies
Altera_Forum
Honored Contributor II
2,068 Views

what is your original clk frequency. Are you aware you are dividing it by 5000*2 (not 5000)

0 Kudos
Altera_Forum
Honored Contributor II
2,068 Views

 

--- Quote Start ---  

what is your original clk frequency. Are you aware you are dividing it by 5000*2 (not 5000) 

--- Quote End ---  

 

 

The original frequency is 50Mhz and yes, Im aware that the frequency divider is actually 10000, I just forgot to explain it in the main post.
0 Kudos
Altera_Forum
Honored Contributor II
2,068 Views

 

--- Quote Start ---  

The original frequency is 50Mhz and yes, Im aware that the frequency divider is actually 10000, I just forgot to explain it in the main post. 

--- Quote End ---  

 

 

so what you say is period of 0.1 msec is actually 0.2msec
0 Kudos
Altera_Forum
Honored Contributor II
2,068 Views

 

--- Quote Start ---  

so what you say is period of 0.1 msec is actually 0.2msec 

--- Quote End ---  

 

 

Sorry for that, you are right! the period was intended to be 0.1ms NOT 0.2 so I reduce the divisor to 2499 and its already working. What I don't really understand is why I have to divide the divider by half, for example if I have a 50MHz clock and it needs to be down to 10KHz, the divider is 5000, right. I understand the code takes a count every rising edge, but if I need 5000 cycles of 20ns (50MHz) to get a 0.1ms period signal why do I need to specify the divider as 2500 if in 5000 cycles, there are 5000 rising edges!
0 Kudos
Altera_Forum
Honored Contributor II
2,068 Views

 

--- Quote Start ---  

Sorry for that, you are right! the period was intended to be 0.1ms NOT 0.2 so I reduce the divisor to 2499 and its already working. What I don't really understand is why I have to divide the divider by half, for example if I have a 50MHz clock and it needs to be down to 10KHz, the divider is 5000, right. I understand the code takes a count every rising edge, but if I need 5000 cycles of 20ns (50MHz) to get a 0.1ms period signal why do I need to specify the divider as 2500 if in 5000 cycles, there are 5000 rising edges! 

--- Quote End ---  

 

 

count1 is meant to divide the 50MHz clock into 10KHz clkout so you need to divide by 5000 (mathematically).  

At implementation using counter you either need 5000/2 counter since at each 2500 count you invert clkout (2500 cycles to invert clkout from 0 to 1 and 2500 cycles to invert from 1 to 0) or use 5000 counting and invert at 2500 and 5000 

 

count2 counts 200 clock edges.so count2 is correct as you are just counting edges of the 10KHz clkout
0 Kudos
Reply