0

I've written a few components to move a stepper motor back and forwards. I've simulated it in modelsim and it works as expected, but it won't work the same in hardware at all.

Basically I have a motor driving component, which takes a command of number of steps, hold time and speed and then performs the movement. Then I have the control_arbiter, which is just an intermediate bridge that connects components wanting access to the motors and the motor driving components. Finally I have a 'search pattern' component, which basically issues the commands to move the motor back and forth.

My problem is that I can't seem to get direction to change when it's running in hardware, regardless of it working in simulation.

Any help with this would be greatly appreciated

Motor driver:

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


entity motor_ctrl is

port(   clk: in std_logic;

        -- Hardware ports
        SCLK, CW, EN: out std_logic;    -- stepper driver control pins

        -- Soft ports
        Speed, steps: in integer;   
        dir: in std_logic;          -- 1 = CW; 0 = CCW;
        hold_time: in integer;      -- if > 0, steppers will be held on for this many clock periods after moving
        ready: out std_logic;       -- indicates if ready for a movement
        activate: in std_logic;     -- setting activate starts instructed motion. 
        pos_out: out integer            -- starts at 2000, 180deg = 200 steps, 10 full rotations trackable
        );

end motor_ctrl;


architecture behavioural of motor_ctrl is

type action is (IDLE, HOLD, MOVE);
signal motor_action: action := IDLE;

signal clk_new: std_logic;
signal position: integer := 2000;

signal step_count: integer := 0;
signal drive: boolean := false;

begin

-- Clock divider
clk_manipulator: entity work.clk_divide port map(clk, speed, clk_new);

-- Drive motors
with drive select
    SCLK <= clk_new when true,
                    '0' when false;

pos_out <= position;

process(clk_new)
    -- Counter variables
    variable hold_count: integer := 0;      
begin

    if rising_edge(clk_new) then
        case motor_action is

            when IDLE =>
                -- reset counter vars, disable the driver and assert 'ready' signal
                hold_count := 0;
                step_count <= 0;
                drive <= false;
                EN <= '0';
                ready <= '1';

                -- When activated, start moving and de-assert ready signal
                if(activate = '1') then
                    motor_action <= MOVE;
                end if;

            when HOLD =>
                -- Stop the step clock signal
                ready <= '0';
                drive <= false; 
                -- Hold for given number of clock periods before returning to idle state
                if(hold_count = hold_time) then
                    motor_action <= IDLE;
                end if;
                -- increment counter
                hold_count := hold_count + 1;

            when MOVE =>                    
                -- Enable driver, apply clock output and set direction
                ready <= '0';
                EN <= '1';
                drive <= true;
                CW <= dir;      

                -- track the position of the motor
                --if(dir = '1') then    
                --  position <= steps + step_count;
                --else
                --  position <= steps - step_count;
                --end if;

                -- Increment count until complete, then hold/idle
                if(step_count < steps-1) then
                    step_count <= step_count + 1;
                else
                    motor_action <= HOLD;
                end if;

        end case;
    end if;

end process;


end behavioural;

Control_arbiter:

entity Control_arbiter is

port (clk: in std_logic;
        EN, RST, CTRL, HALF, SCLK, CW: out std_logic_vector(2 downto 0)
        -- needs signals for levelling and lock
        );

end Control_arbiter;


architecture fsm of Control_arbiter is

type option is (INIT, SEARCH);
signal arbitration: option := INIT;

-- Motor controller arbiter signals
-- ELEVATION
signal El_spd, El_stps, El_hold, El_pos: integer;
signal El_dir, El_rdy, El_run: std_logic;


-- Search signals
signal search_spd, search_stps, search_hold: integer; 
signal search_dir, search_Az_run, search_El_run: std_logic := '0';
-- status
signal lock: std_logic := '0';

begin

-- Motor controller components
El_motor: entity work.motor_ctrl port map(clk, SCLK(0), CW(0), EN(0),
                                                        El_spd, El_stps, El_dir, El_hold, El_rdy, El_run);                                                      


-- Search component
search_cpmnt: entity work.search_pattern port map(  clk, '1', search_dir, search_stps, search_spd, search_hold, 
                                                                    El_rdy, search_El_run);


process(clk, arbitration)

begin

    if rising_edge(clk) then

        case arbitration is

            when INIT =>
                -- Initialise driver signals
                EN(2 downto 1) <= "11";
                CW(2 downto 1) <= "11";
                SCLK(2 downto 1) <= "11";
                RST  <= "111";
                CTRL <= "111";
                HALF <= "111";
                -- Move to first stage
                arbitration <= SEARCH;

            when SEARCH =>
                -- Map search signals to motor controllers
                El_dir  <= search_dir;
                El_stps <= search_stps;
                El_spd  <= search_spd;
                El_hold <= search_hold;
                El_run  <= search_El_run;
                -- Pass control to search
                -- Once pointed, begin search maneuvers
                 -- map search signals to motor controllers
                 -- set a flag to begin search
                 -- if new pointing instruction received, break and move to that position (keep track of change)
                 -- On sensing 'lock', halt search
                 -- return to holding that position


        end case;

    end if;

end process;


end fsm;

Search Pattern:

entity search_pattern is

generic (step_inc: unsigned(7 downto 0) := "00010000"
            );
port (clk: in std_logic;
        enable: in std_logic;
        dir: out std_logic;
        steps, speed, hold_time: out integer;
        El_rdy: in std_logic;
        El_run: out std_logic
        );

end search_pattern;


architecture behavioural of search_pattern is

type action is (WAIT_FOR_COMPLETE, LATCH_WAIT, MOVE_EL_CW, MOVE_EL_CCW);
signal search_state: action := WAIT_FOR_COMPLETE;
signal last_state: action := MOVE_EL_CCW;

begin

hold_time <= 1; 
speed <= 1;
steps <= 2;

process(clk)

begin

    if rising_edge(clk) then

        -- enable if statement

            case search_state is

                when LATCH_WAIT =>
                    -- Make sure a GPMC has registered the command before waiting for it to complete
                    if(El_rdy = '0') then       -- xx_rdy will go low if a stepper starts moving
                        search_state <= WAIT_FOR_COMPLETE;      -- Go to waiting state and get ready to issue next cmd
                    end if;

                when WAIT_FOR_COMPLETE =>

                    -- Wait for the movement to complete before making next
                    if(El_rdy = '1') then       
                        -- Choose next command based on the last
                        if last_state = MOVE_EL_CCW then
                            search_state <= MOVE_EL_CW;
                        elsif last_state = MOVE_EL_CW  then
                            search_state <= MOVE_EL_CCW;
                        end if;
                    end if;             

                when MOVE_EL_CW =>
                    dir <= '1';
                    El_run <= '1';
                    last_state <= MOVE_EL_CW;
                    search_state <= LATCH_WAIT;

                when MOVE_EL_CCW =>
                    dir <= '0';
                    El_run <= '1';
                    last_state <= MOVE_EL_CCW;
                    search_state <= LATCH_WAIT;

                when others =>
                    null;
            end case;

        -- else step reset on not enable

    end if;

end process;

end behavioural;        

Sim: https://i.stack.imgur.com/qJbLU.png

Kureigu
  • 259
  • 4
  • 12
  • You are more likely to get answers from VHDL pros at http://electronics.stackexchange.com/. – Lundin Mar 21 '13 at 14:18
  • I've just glanced at the code quickly - and noticed that in your control arbiter you have a clocked process that is also sensitive to the `arbitration` signal. Clocked processes should only be sensitive to a clock signal, and possibly an asynchronous reset. You might try investigating that further... – sonicwave Mar 21 '13 at 14:31
  • When simulation and real-life diverge: http://stackoverflow.com/a/15204291/106092 – Martin Thompson Mar 21 '13 at 16:15

1 Answers1

1

scanning quickly through your code, there are some things that you should change for synthesis:

1) clock divider: make your motor_driver process sensitive to clk instead of clk_new. to divide the clock, generate a one-clock-cycle enable signal every n clocks. the begin of the process could look as follows:

    process(clk)
        ... 
    begin

    if rising_edge(clk) then
        if enable='1' then
            case motor_action is
             ...

2) initializations of the form

    signal position: integer := 2000;

only work for simulations but don't work for synthesis. for initialization in synthesis use a reset signal within the process.

3) add to all your state machines a "when others" clause, where the state is set to a defined value (e.g. search_state<=INIT).

baldyHDL
  • 1,387
  • 1
  • 10
  • 16
  • I think I see what you're saying mostly, but I'm having real difficulty understanding how I will generate that clock enable. Would it just be through counting clock edges in a separate process and triggering the enable when it reached the desired value? – Kureigu Mar 22 '13 at 10:16
  • Yes, if the counter reaches the target value, restart the counter an set the enable signal active. make sure that you activate enable only for one clk cycle! – baldyHDL Mar 22 '13 at 14:19
  • I've now tried this, but I still have the same problem. Whatever I do the 'dir' output will still not change despite it changing fine in simulation time after time. If anyone's interested, [here's a zip of the components](https://www.dropbox.com/s/bsy2vgwuv6wt8uh/TB_search_pattern.zip) – Kureigu Mar 26 '13 at 11:43
  • add to all your state machines a "when others" clause, where the state is set to a defined value (e.g. search_state<=INIT). – baldyHDL Mar 26 '13 at 12:42
  • Thank you so much. I added INIT cases for each FSM and pointed the 'when others' case at it. That seems to have somehow done the trick! Can you update your answer with that added on so that in future other people might see it? – Kureigu Mar 26 '13 at 16:34