NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Basic Electronics » Stepper Motor Direction Control

July 11, 2012
by Ralphxyz
Ralphxyz's Avatar

By popular demand lets start a new thread on stepper motor direction control :-)

Continued on from my Stepper Motor Programing thread, which has been very informative and interesting.

Ralph

July 11, 2012
by Ralphxyz
Ralphxyz's Avatar

Here is my initial "untested" direction code:

    #define F_CPU 14745600

    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/pgmspace.h>
    #include <stdio.h>
    #include <avr/interrupt.h>

    #include "../libnerdkits/io_328p.h"   
    #include "../libnerdkits/delay.h"
    #include "../libnerdkits/lcd.h"
    #include "../libnerdkits/uart.h"

    #define AB         ((1<<PB0) | (1<<PB2))         
    #define A_Bneg     ((1<<PB0) | (1<<PB3))
    #define Aneg_Bneg  ((1<<PB1) | (1<<PB3))
    #define Aneg_B     ((1<<PB1) | (1<<PB2))
    #define A          (1<<PB0)
    #define Aneg       (1<<PB1)
    #define B          (1<<PB2)
    #define Bneg       (1<<PB3)

    #define dTime1 10
    #define co_pins  (1<<PB0|1<<PB1|1<<PB2|1<<PB3)

    int main()
    { 
        //DDRB = 0b11111111;    
        DDRB = co_pins;
        while (1) 
        {

/*
 * To change modes add a / to the start of a Mode line 
 * i.e. make it a commented line //
*/

/*  CW Wave Mode  maintain 4 MSB        A,B,/A,/B   
            PORTB = ((PORTB |co_pins) & A);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & B); 
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg);
            delay_ms(dTime1); 
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Bneg);
            delay_ms(dTime1);
//*/
/*  CW Full Step maintain 4 MSB      AB, /AB, /A/B, A/B
            PORTB = ((PORTB |co_pins) & AB);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_B); 
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_Bneg);
            delay_ms(dTime1); 
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & A_Bneg);
            delay_ms(dTime1);           
//*/    
/*  CW Half Step Mode       maintain the 4 MSB      
        //AB, B, /AB, /A, /A/B, /B, A/B, A

            PORTB = ((PORTB |co_pins) & AB);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & B);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_B);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Bneg);
            delay_ms(dTime1);
            PORTB=((PORTB |co_pins)& A_Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & A);
            delay_ms(dTime1);

//*/

/*  CCW Wave Mode
        /B,/A,B,A

            PORTB = ((PORTB |co_pins) & Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg);
            delay_ms(dTime1); 
            PORTB = ((PORTB |co_pins) & B); 
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & A);
            delay_ms(dTime1);

//*/

/*  CCW Full Step Mode

        //  A/B, /A/B, /AB, AB

            PORTB = ((PORTB |co_pins) & A_Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_Bneg);
            delay_ms(dTime1); 
            PORTB = ((PORTB |co_pins) & Aneg_B); 
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & AB);
            delay_ms(dTime1);

//*/

/*  CCW Half Step Mode
        //  A, A/B, /B, /A/B, /A, /AB, B, AB

            PORTB = ((PORTB |co_pins) & A);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins)& A_Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_Bneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & Aneg_B);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & B);
            delay_ms(dTime1);
            PORTB = ((PORTB |co_pins) & AB);
            delay_ms(dTime1);

//*/

    }

    return 0;
}

I "plan" on using a CW and CCW function to be called with button press.

I would like the motor to stay on as long as the button is pressed.

Of course turning the motor on with first press and stopping the motor with next press would also be a good option.

I need a rainy day to test my code.

Ralph

July 12, 2012
by Ralphxyz
Ralphxyz's Avatar

Well CCW Full Step Mode works!!

Ralph

July 12, 2012
by Ralphxyz
Ralphxyz's Avatar

All three modes work!

Ralph

July 12, 2012
by sask55
sask55's Avatar

Hi Ralph

At the risk of causing more confusion, I would suggest a new approach to establish directional control in any of the three motor modes in either direction. You could make use of a one dimensional array variable to hold the output pin conditions for each possible step. By doing this you can slightly reduce the amount of code and makes it easy to pick up the sequence in the correct spot after a stop and restart. I also would combine all three motor control modes into one sequence of steps.

I will attempt to explain

Dimension a variable

int steps[8];

Assign each element in that array a value corresponding to the motor steps in your #define statements. Make the order of the array elements the same as the order of the half stepping mode. something like this

steps[0] = ((1<<PB0) | (1<<PB2)) ;      /  sets step AB 
steps[1]= (1<<PB2);         / sets step B
steps[2]=  ((1<<PB1) | (1<<PB2));     /sets step /AB
steps[3]= (1<<PB1);         /sets step/A
steps[4]= ((1<<PB1) | (1<<PB3));     / sets step/A/B
steps[5]= (1<<PB3);         /sets step /B
steps[6]= ((1<<PB0) | (1<<PB3));     /sets step A/B
steps[7]= (1<<PB0);         /sets stepA

set up and int variable to hold the step value that is current.

uint8_t  step;

use the step variable to select what element of steps you want to set. ie what step to send to the controller

PORTB = ((PORTB |co_pins) & steps[step] );

by moving thru the elements of this array counting upwards or downwards the motor will move in either of the two directions.

set up and int variable to determine the direction the motor will spin.

uint8_t  direct =1;

set up a button to toggle the value of direct to ether 1 or -1 each time you push the button. Each time the button is pushed do

direct = - direct;

Increment the variable "step" by the value of the variable "direct" to go to the next motor step in the sequence CCW or CW.

step = step + direct;
    if (step == 8){
    step =0};
    if (step == -1){
    step =7};

By using a loop to repeat the same PORTB = ((PORTB |co_pins) & steps[step] ); line, over and over after each increment of "step" you can step either way true the half step mode and easily start where you left off after a stop.

The 8 steps in the half step mode are just a combination of the other two modes overlapping each other. by using only the even numbered elements in the steps array we have full stepping by using only the odd elements we have wave stepping. by adding a few of more lines of code you could set up a button to select the mode you wish to run. Each time you push the button you would toggle a variable to one of three values. the three values would correspond to one of the three modes. By selecting the full step mode you would have the code skip over all the odd numbered elements in the array. by selecting the wave mode you would skip over all of the even elements in the array , half mode would send each element of the array to the controllor

I am not sure this is very clear. I will try to set up a nerd kit board next week if I get the time. I will write a sample code to do both the direction control and the speed setting and post it when I can. I make way too many typos and simple syntax errors to post a code without trying to compile and run it first.

Darryl

July 12, 2012
by sask55
sask55's Avatar

already I noticed a error

      int steps[8];

should be

     uint8_t steps[8];

Darryl

July 12, 2012
by sask55
sask55's Avatar

also

 uint8_t  direct =1;

should be

  int8_t direct =1;
July 12, 2012
by live4the1
live4the1's Avatar

What would be the best way to write the code for the stop button that would allow the CCW or CW buttons to start where the motor stopped? Would interrupts need to be used?

July 12, 2012
by sask55
sask55's Avatar

live4the1

No interrupts are not required, they would be better especially for very slow motor speeds. With slow motor speeds the MCU will be doing relatively long delay functions and therefore will not be looping back as often to check the status of the buttons. so there could be some short delays and the MCU may actually miss quick button pushes when the motor in turning very slowly.

With the method I have proposed there is really only one button to control CW or CCW. Each time that button is pressed it would reveres the motor direction. This could be done when the motor is stopped or when it is turning. You could use two buttons but they are both kind of doing the same thing ie changing the direction.

The way I intend to write the code there will be five push buttons
A run / stop button that toggles between running the motor or not with each push.

A CW / CCW button that toggles between the two direction with each push

A mode button that toggles thru the three possible stepper drive modes

A exponentially increasing speed increase button.

and an exponentially increasing speed decrease button.

The reason I have proposed using a array variable to hold the motor steps is to make restarting in the same place very easy. All the steps would be sequentially numbered in the correct order. There is a number in a variable that holds the number of the step that you are on. It is just a matter of moving up one step or down to turn CW or CCW.

Darryl

July 16, 2012
by live4the1
live4the1's Avatar

I believe that

uint8_t  step;

should be

int8_t step;

because of

if (step == -1)

I learned this after many tries and finally saw it.

July 21, 2012
by sask55
sask55's Avatar

live4the1

I just got back to this thread again.

As you have pointed out I made a number of errors in my code examples earlier in this thread. It is very apparent that I should not try and post bits and pieces of code using only the notepad type text editor. After I have written some code using the programmers notepad I usually kind of run thru the logic and step order in my mind and correct things like variable overflows, variable types required, count errors and things like that. Doing a quick conceptual overview where I am mostly thinking about how to achieve an objective does not result in correct code details. Sorry about the errors.

Darryl

July 21, 2012
by live4the1
live4the1's Avatar

Don't be sorry, I enjoyed working through it and actually finding the problem.

Post a Reply

Please log in to post a reply.

Did you know that you can turn a $20 digital scale into a live weight sensor using our kit with a few extra parts? Learn more...