[SOLVED] Controlling a servomotor " SG90 9g Mirco Servo " with Basys 3

Status
Not open for further replies.

vlad97

Newbie level 1
Joined
May 28, 2018
Messages
1
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
16
Hi there!

I'm working on a home-made project with a servomotor using Basys 3 . I need to control my servomotor in that way that it can rotate 90° back and forth several times.

My servomotor is a SG90 9g Micro Servo and I'm tryng to write a code in VHDL to control it. The problem is that i don't know how to make the division for the 2 timers so that the servomotor can rotate 90° .

I can hear the servo, but he can't rotate 90° ..

I'm not using the Module Pmod Con 3 because in my country i can't buy such a module . I am using a breathboard, and the 3 wires from the Servo are connected to a Pmod Connector on basys 3, using the breathboard so : Pin 6 for VCC, Pin 5 for GND, Pin 1(PWM) for JA1 .



Regards,

Vlad
 

Attachments

  • Servo.rar
    2.8 KB · Views: 179


Hello Vlad97,

I take a look into your code, and i think that you need PWM generator where on inputs you put "duty" of generated PWM. My answer is not direct because i don't fully understand code in entity: servoG Instead of altering your code I give you working PWM genertator:
Code:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY pwm IS
  GENERIC(
      sys_clk         : INTEGER := 1_000_000; --system clock frequency in Hz
      pwm_freq        : INTEGER := 60_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_new  :  INTEGER RANGE 0 TO period/2 := 0;              --number of clocks in 1/2 duty cycle
  TYPE half_duties IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period/2; --data type for array of half duty values
  SIGNAL  half_duty    :  half_duties := (OTHERS => 0);                     --array of half duty values (for each phase)
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_new <= 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
          half_duty(i) <= half_duty_new;                                         --set most recent duty cycle value
        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(i)) 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(i)) 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;

This is generic module, so in section GENERIC you can parametrize the generator:

Code:
  GENERIC(
      sys_clk         : INTEGER := 1_000_000; --system clock frequency in Hz
      pwm_freq        : INTEGER := 60_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

As upy can see I have incoming clock frequency set to 1 MHz and PWM frequency to 60 Khz, bits for duty cycle to 8 bit and number of phases to 1. You can change these values in your design.

You have to put component pwm to your top entity like that:

Code:
  component pwm is
      Port (  clk : in  STD_LOGIC;
           reset_n : in  STD_LOGIC;
           ena : in  STD_LOGIC;
           duty : in  STD_LOGIC_VECTOR (7 downto 0);
           pwm_out : out  STD_LOGIC_VECTOR (0 downto 0);
           pwm_n_out : out  STD_LOGIC_VECTOR (0 downto 0));
  end component;  

and after begin:

C7: pwm port map (clk_1MHz, RST, '1', data1_signal, pwm1_out, pwm1_n_out);

where in data1_signal you are giving current "duty". On pwm_out you have pwm pulse.

What you need to do is give duties from 0 to 98 value and then from 98 to 255 (in series of several times) - it should move servo as you want. I has used these code im my project and it is working fine. You can simulate ROM memory in VHDL array (values of duties series) similary as in this example:

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY_WYPELNIEN is
  generic (
		NBit : natural := 30;
		Div : natural := 3_000_000 --3s
		);
  port ( clk :in std_logic;
         pushB :in std_logic;
			blokada :in std_logic;
			rst : in  STD_LOGIC; --aktywne 0
			timerO : out std_logic;
         data : out std_logic_vector(7 downto 0) );
end WEKTORY_WYPELNIEN;

 architecture BEHAVE of WEKTORY_WYPELNIEN is

  signal ADDRESS :integer range 0 to 32 :=0;
   signal TEMP :integer range 0 to 10_000_000 :=0; -- liczba impulsów
   -- jaka ma minąć zanim dojdzie do zmiany adresu

  	signal GENERACJA_SERII_WEKTOROW : std_logic :='0'; 
   -- okreslanie kiedy ma sie zaczac generacja serii wektorow
	signal GENERACJA2_SERII_WEKTOROW : std_logic :='0'; 

   signal KONIEC_GENERACJI_WEKTOROW : std_logic :='0'; 
   -- okreslenie kiedy koniec generacji serii wektorow

   signal OPOZNIENIE_BUTTONA :integer range 0 to 1_000_000 :=0;
	
	signal cnt: std_logic_vector(Nbit -1 downto 0) := (others=>'0'); --licznik timera czasu otwarcia
	
	type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
  constant my_Rom : mem := (
    0  => "00000011",
    1  => "00000111",
    2  => "00001110",
    3  => "00001111",
    4  => "00010101",
    5  => "00011100",
    6  => "00100011",
    7  => "00010000",
    8  => "00101010",
    9  => "00110001",
    10 => "00111000",
    11 => "00111111",
    12 => "01000110",
    13 => "01001101",
    14 => "01010100",
    15 => "01011011",
    16 => "01100010",
    17 => "01101001",
    18 => "01110000",
    19 => "01110111",
    20 => "01111110",
    21 => "10000101",
    22 => "10001100",
    23 => "10010011",
    24 => "10011010",
    25 => "10100001",
    26 => "10101000",
    27 => "10010111",
    28 => "10110110",
    29 => "11011001",
    30 => "11100000",
    31 => "11111111"
	 );

begin

    --  ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
   process (CLK)
   begin
      if rising_edge(CLK) then
         if (rst= '0') then
			   ADDRESS <= 0;
				TEMP <= 0; -- liczba impulsów
  	         GENERACJA_SERII_WEKTOROW <= '0'; 
	         GENERACJA2_SERII_WEKTOROW <= '0'; 
            KONIEC_GENERACJI_WEKTOROW <= '0'; 
            OPOZNIENIE_BUTTONA <= 0;	
			else	
              -- SPRAWDZENIE CZY PRZYCISK JEST NACISNIETY CZY NIE
              if (pushB = '0') then -- lub '0' gdy po nacisnieciu
              -- przycisku generowane jest przerwanie
                  if (OPOZNIENIE_BUTTONA < 10_000) then
                  -- opoznienie dzialania przycisku, aby za szybko 
                  -- nie szla generacja po nacisnieciu
                     OPOZNIENIE_BUTTONA <= OPOZNIENIE_BUTTONA + 1;
                  else
                     if (KONIEC_GENERACJI_WEKTOROW = '0') then  
                     -- jesli jest koniec to nie wznawiaj generacji
                         GENERACJA_SERII_WEKTOROW <= '1';
								 GENERACJA2_SERII_WEKTOROW <= '0';
                     end if;
                  end if;
              else
                  KONIEC_GENERACJI_WEKTOROW <= '0';  
                   OPOZNIENIE_BUTTONA <= 0;
                  -- zeruj  koniec, gdy przycisk jest puszczony
                  -- zeruj opoznienie przycisku
              end if;             

              -- GENERACJA SERII WEKTOROW PO NACISNIECIU PRZYCISKU 
              -- ORAZ OKRESLENIE JEJ KONCA PO JEJ WYKONANIU
              if (GENERACJA_SERII_WEKTOROW = '1') then
						if (TEMP < 20_000) then --okolo 0,02 s : 0,64 s calosc
                      TEMP <= TEMP + 1;
						else 
                      TEMP <= 0;
                      if (ADDRESS < 31) then
                         ADDRESS <= ADDRESS + 1;
                     else
                         KONIEC_GENERACJI_WEKTOROW <= '1';
                         GENERACJA_SERII_WEKTOROW <= '0';
								 GENERACJA2_SERII_WEKTOROW <= '1';
                         ADDRESS <= 0;
                     end if;
						end if;
              else
					  
                 TEMP <= 0;
              end if;
			  end if;	  
      end if;
   end process;


      -- GENEROWANIE WEKTOROW WYPELNIEN
   process (CLK)
   begin
      if rising_edge(CLK)  then
		if (rst= '0') then
			DATA <= "00000000";
		elsif (blokada = '0') then
			DATA <= "00000000";
		else
	    if (GENERACJA_SERII_WEKTOROW = '1') and (GENERACJA2_SERII_WEKTOROW = '0')  then-- gdy trwa generacja             -- wektorow
               DATA <= my_rom(ADDRESS);
		 else if 	(GENERACJA2_SERII_WEKTOROW = '1') then
					DATA <= "11100000";
	    else  -- gdy generacja nie trwa
          DATA <= "00000000";
			 end if;
		 end if;
	end if;
	end if;
   end process;
	
	   -- Timer do odliczania czasu otwierania
		
	process(CLK)
	begin
		if (CLK'event and CLK='1') then
		  if ((rst = '0') or (blokada = '0')) then 
				cnt <= (others => '0');
				timerO  <= '0';
		  else
			if ((GENERACJA_SERII_WEKTOROW = '1') or (GENERACJA2_SERII_WEKTOROW = '1')) then		  
				if cnt < Div then
					--timerO <= '0';--Check this
					cnt <= cnt+1;
				else
					cnt <= (others=>'0');
					timerO <= '1';
				end if;
			end if;
		  end if;
		end if;
   end process;			
	
end BEHAVE;

I am aware that in the last code is in many factors not clear for you, but the main idea is simply (it was used for soft-start generator of DC engine). You attache data output from this entity to duty input in pwm generator. Of course you have to alter this code to your needs - it is only example. I decided to answer because nobody other give you any hint. i hope it can be useful. Entity pwm is one of IP cores from opencores.org, but I dont remember what exactly.

Regards
 
Last edited:
Reactions: vlad97

    vlad97

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…