Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[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 .

Untitled.png

Regards,

Vlad
 

Attachments

  • Servo.rar
    2.8 KB · Views: 176

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 .

View attachment 146926

Regards,

Vlad

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:
  • Like
Reactions: vlad97

    vlad97

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top