Full Version : Carl's Step-Motor Control w Accel & Deaccel (GCC)
avr >>MOTORS SERVOS & PWM >>Carl's Step-Motor Control w Accel & Deaccel (GCC)


AVR_Admin- 04-22-2006
I am providing this code to show how I was able to achieve fixed acceleration and deceleration using the Mega64 and a GeckoDrive G21 stepper motor controller.

This stepper motor controller is a STEP/DIRECTION controller dirving an Automation Direct 270 Oz/In 3 amp bi-polar stepping motor.

AVRFreak's Link: http://www.avrfreaks.net/index.php?name=PN...ewtopic&t=37891

Carl W. Livingston, KC5OTL
microcarl@hughes.net

AVR_Admin- 04-23-2006
CODE

// This program was written by Carl W Livingston, KC5OTL.
// I freely grant permission to use this program for personal use only.
// No commercial use of this program is allowed unless written authorization
// is granted by the author, Carl W. Livingston, KC5OTL.
// If changes are made to this program, due credit must be given to the
// original author, Carl W. Livingston, KC5OTL.

// While this program may migrate to the global public domain, this program
// was written for my personal use and is released to www.AVRFreaks.net for
// the personal use of the members of www.AVRFreaks.net.  If this program is
// posted on web-sites other then www.AVRFreaks.net, full, and due credit MUST be
// given to the Author known as microcarl, who is an www.AVRFreaks.net member,
// and whose full and legal name is Carl W. Livingston.

/*********************************************************************************
// DISCLAIMER!!!
// The author of this program assumes no liability for damage, monitary loss, or
// programming errors in this program.  The use of this program is at the risk
// of the end user.
   

/*
This stepper motor program is currently set up on an Atmel STK500/STK501
development borad using a GeckoDrive G210 stepper motor drive.
The GeckoDrive stepper motor controller uses STEP/DIRECTION inputs to
control the stepper motor velocity and direction.

The program was written using the ImageCraft ICCAVR V7.xx C complier.  Code
compression was used during compilation of the program.

This program has been tested on the Mega64.  I have also added conditional
compilation clauses to use the Mega16 and Mega32 microcontrollers.

Using the ImageCraft ICCAVR V7.00 compiler with code compression, the
Mega16 compiles out at about 60% usage of its FLASH memory, the Mega32 at
about 40% and, the Mega64 compiles at about 11%.

While this program uses the STEP/DIRECTION output format it can be easily
converted to utilize a bi-polar steppr motor with the use of an appropriately sized H-Bridge
and a uni-polar stepper motor using appropriately sized power MOS-FETS.  Future
releases of this program will be released showing the use of bi-polar and
uni-polar stepper motors.

While configuring a stepper motor to move in a forward or reverse direction is
somewhat trivial, I found that the acceleration and deceleration aspects was not.

This stepper motor program uses fixed acceleration and deceleration rates.
Future releases will also include adjustable acceleration and deceleration
If the sum of the fixed acceleration and deceleration counts are greater then
the move counts, the move velocity is controlled, along with the fixed acc/dec
counts to form a triangle move profile rather then the typical trapazoid move
profile.

In addition, if the commanded move velocity is slower then the minimum velocity
that will reliably move the stepper motor, no acceleration/deceleration is used.

There are conditional compilation statements, as well, that allow the use of
HyperTerminal to display the basic position, step rate and other useful status
information.  The program line containing "debug" define at the top of the StepCon.c
program (this file) file headder area can be blocked out to turn off the debug code.

The command protocol is as follows:

m = set axis move count.
v = set axis velocity count.
h = home axis at zero position count.
c = cycle start.
s = controlled axis stop.
p = display axis position.
r = report axis status.

Axis moves are in step counts.  A signed long integer are used for position accumulation
Axis velocity is in counts and is based on the microcontroller clock frequency. Timer1
is used ablon with OCR1A for velocity control.
*/

// FILE NAME:     StepCon.c

/*************************************************************/
// Standard C library Includes
// Change the following line to iom16v.h or iom32v.h
#include <iom64v.h>
#include <macros.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// StepCon motion controller Includes
// The following two files should reflect changes to support the USART
// differences between the Mega16, Mega32 and Mega64.
// The Mega16 and Mega32 use the generic USART defines.
// The Mega64 USART has USART0 and USART1.  For the Mega64, USART1 is used
// to output axis and status information to HyperTerminal.

#include "getchar.c"
#include "putchar.c"

/*************************************************************/
/*********************** DEBUG CONTROL ***********************/
/*************************************************************/
#ifndef DEBUG
// Block out the following line to turn off the debug features.
#define DEBUG
#endif DEBUG
/*************************************************************/
/*************************************************************/
/*************************************************************/

/*************************************************************/
// StepCon motion controller Defines

// Axis status byte flags
#define FIRST_HOME_FLAG      0x01   // Not homed after power up
#define AT_HOME_FLAG         0x02   // Axis Home Flag
#define IN_POSITION_FLAG   0x04   // Axis In Position Flag
// #define Future use      0x08   // Future use
#define MOVE_FLAG            0x10   // Axis Move Enable Flag
#define ACC_FLAG            0x20   // Axis Acceleration Enable
#define DEC_FLAG            0x40   // Axis Deceleration Enable
#define   DIR_FLAG         0x80   // Axis Direction FLAG

// PORTC I/O definitions
#define AXIS_NOT_HOMED      0x01   // Not homed after power up
#define AXIS_AT_HOME         0x02   // Axis Home
#define AXIS_IN_POSITION   0x04   // Axis In position
#define AXIS_PLUS_LIMIT      0x08   // (+) Axis limit switch
#define AXIS_MINUS_LIMIT   0x10   // (-) Axis limit switch
#define AXIS_ENABLED      0x20   // Future use
#define   AXIS_STEP      0x40   // Axis "STEP" Control Bit
#define   AXIS_DIR      0x80   // Axis "DIRECTION" Control Bit

#ifdef DEBUG
/****************************************************************************/
// Macro definitions

// Clear the communications terminal
#define CLEAR_SCREEN (printf("%c[2J", ESC))

// Position cursor on communications terminal. Macro inputs, x = column, y = row
#define PUT_CURSOR(x, y)  (printf("%c[%d;%dH", ESC, y, x))

/****************************************************************************/
#endif DEBUG

void InitDevices(void);
void SetVelocity(void);
char *GetData(char *);

char AxisStatus;
char cmd = NULL;
char COMMAND_DATA[10];

unsigned int   T1 = 250;               // Acceleration time. Default = 250 counts
unsigned int   T3 = 250;              // Deceleration time. Default = 250 counts
unsigned int   CommandVelocity = 100;  // Commanded velocity. Default = 100
unsigned int   TargetVelocity;          // Final move velocity. Processed by command input
unsigned int     StartingVelocity = 500; // Starting velocity for move. Default = 500

#ifdef DEBUG
long int      TempCount;
#endif DEBUG

long int      TargetCount = 0;
long int      CurrentCount = 0;
long int      TriggerCount;

/****************************************************************************/
// This is the Stepping motor controller main program.

void main(void)
{   // Initialize the microcontroller hardware function blocks.
   InitDevices();

  // Initialize system variables.
#ifdef __iom16v_h
  UDR = NULL;
#endif __iom16v_h

#ifdef __iom32v_h
  UDR = NULL;
#endif __iom32v_h

#ifdef __iom64v_h
  UDR1 = NULL;
#endif __iom64v_h

  cmd = NULL;

  AxisStatus = NULL;
  PORTC = NULL;
   
#ifdef DEBUG    
  CLEAR_SCREEN;

  // Set up the communications terminal screen text.
  PUT_CURSOR (20, 14);
  printf("Feed Rate = ");

  PUT_CURSOR(20, 16);
  printf("Target Position = ");

  PUT_CURSOR(20, 18);
  printf("Current Position = ");

  PUT_CURSOR(1, 16);
  printf("Home: ");
   
  PUT_CURSOR(1, 18);
  printf("In Position: ");
#endif DEBUG

  SEI(); // Enable the global interrupts.
                       
  // Repeat this loop forever!
  do
  {

#ifdef DEBUG    
    // Update axis status to the communications terminal.
    PUT_CURSOR(40, 14);
    TempCount = CommandVelocity;
      printf("%6u ", TempCount);
   
    PUT_CURSOR(40, 16);
    TempCount = TargetCount;
      printf("%6ld ", TempCount);
     
    PUT_CURSOR(40, 18);
    TempCount = CurrentCount;
      printf("%6ld ", TempCount);

    PUT_CURSOR(14, 16);
    if (AxisStatus & AT_HOME_FLAG)
        printf("Y");
    else printf("N");
       
    PUT_CURSOR(14, 18);
      if (AxisStatus & IN_POSITION_FLAG)
       printf("Y");
    else printf("N");        
#endif DEBUG
         
/*
 :                          if (TARGET_COUNT > (T1 + T2))                         :  
 :                       TARGET_VELOCITY = COMMAND_VELOCITY                       :  
 :      :                                                                  :      :  
 :  T1  :                                T2                                :  T3  :  
 :      :                                                                  :      :  
V :      :                                                                  :      :  
E :      :                                                                  :      :  
L :      :__________________________________________________________________:      :  
O :     /:                                                               ___:\     :  
C :    / :                                        DECELERATION WINDOW __/   : \    :  
I :   /  :___                                                               :  \   :  
T :  /   :   \__ TARGET VELOCITY = COMMAND VELOCITY                         :   \  :  
Y : /    :                                                                  :    \ :  
 :/     :                                                                  :     \:  
 :------:------------------------------------------------------------------:------:  
 :                                                                                :  
 :    __ STARTING VELOCITY                                                        :  
 :___/                                                                            :  
 :   \__ STARTING COUNT                                                           :  
 :                                                                                :  
 :         >------------------------- DISTANCE ------------------------->         :  

 :                       if (abs(TARGET_COUNT) <= (T1 + T2))                      :  
 :          TARGET_VELOCITY = STARTING_VELOCITY - (abs(TARGET_COUNT) / 2)         :  
 :                                                                                :  
 :      :      :                                                                  :  
 :  T1  :  T3  :                                                                  :  
 :      :      :                                                                  :  
V :      :  ____:___________________ COMMAND VELOCITY ______________________       :  
E :      : /    :                                                                  :  
L :      :/    _:_ TARGET VELOCITY                                                 :  
O :      /____/ :                                                                  :  
C :     /:\   \_:_ MODIFIED DECELERATION WINDOW                                    :  
I :    / : \    :                                                                  :  
T :   /  :  \   :                                                                  :  
Y :  /   :   \  :___                                                               :  
 : /    :    \ :   \__ TARGET COUNT                                               :  
 :/     :     \:                                                                  :  
 :------:------:------------------------------------------------------------------:  
 :                                                                                :  
 :    __ STARTING VELOCITY                                                        :  
 :___/                                                                            :  
 :   \__ STARTING COUNT                                                           :  
 :                                                                                :  
 :         >------------------------- DISTANCE ------------------------->         :  

***********************************************************************************
*            Target                  Reference                   Target           *
*            Count                     Count                     Count            *
*              |                         |                         |              *
*         (-)  |  (+)               (-)  |  (+)               (-)  |  (+)         *
* ---------|---N---|-----------------|---0---|-----------------|---N---|--------- *
*        __|       |__             __|       |__             __|       |__        *
*       |             |           |             |           |             |       *
*    Forward       Reverse     Forward       Reverse     Forward       Reverse    *
*     Decel         Decel       Decel         Decel       Decel         Decel     *
*    Trigger       Trigger     Trigger       Trigger     Trigger       Trigger    *
*                                                                                 *
*            >-----------------  Forward  Direction  ----------------->           *
***********************************************************************************

 If the axis is moving forward, the "Forward Deceleration Trigger" value must be
 subtracted from the "Target Count", as indicated by the (-).

 If the axis is moving in reverse, the "Reverse Deceleration Trigger" value must be added
 to the "Target Count", as indicated by the (+).

 The result of the above addition or subtraction then becomes the forward or
 reverse deceleration trigger point.

 The distance between the "Target Count" and the "Forward" or "Reverse" trigger
 count is the deceleration window and is where the axis deceleration takes place.
*/
      // Check for a character, but don't wait.    
#ifdef __iom16v_h
    if (UCSRA & (1<<RXC))
        cmd = UDR;
#endif __iom16v_h
   
#ifdef __iom32v_h
    if (UCSRA & (1<<RXC))
        cmd = UDR;
#endif __iom32v_h
     
#ifdef __iom64v_h
    if (UCSR1A & (1<<RXC1))
        cmd = UDR1;
#endif __iom64v_h

     switch (cmd)
     {
        case 'm':   // Get the new axis target count.
                  if (!(AxisStatus & MOVE_FLAG))
                 {  
                    TargetCount = strtol(GetData(COMMAND_DATA),0,NULL);

                    if (TargetCount > CurrentCount)                      
                    {  // Go forward.
                         PORTC &= ~AXIS_DIR;
                         AxisStatus &= ~DIR_FLAG;
                    }
                    else
                    {  // Go reverse.
                            PORTC |= AXIS_DIR;
                         AxisStatus |= DIR_FLAG;    
                    }
                     
                    SetVelocity();
                 }
                 cmd = NULL;
                 break;

        case 'p':   // Report the current step count.
                  #ifdef DEBUG
                 PUT_CURSOR(40, 18);
                 #endif DEBUG
                   
                 printf("%6ld  ", CurrentCount);                    
                 cmd = NULL;
                 break;

        case 'r':   // Report the current axis status.
                  #ifdef DEBUG
                  PUT_CURSOR(1, 20);
                 #endif DEBUG
                   
                 printf("%x ", AxisStatus);
                 cmd = NULL;
                 break;
                   
        case 'c':   // Cycle Start.
                  if (!(AxisStatus & MOVE_FLAG))
                    if (TargetCount != CurrentCount)                            
                    {
                      if (CommandVelocity > 500)
                          OCR1A = CommandVelocity;
                      else
                              OCR1A = StartingVelocity;
                         
                         AxisStatus |= (ACC_FLAG | MOVE_FLAG);
                         TIMSK |= (1<<OCIE1A); // Enable timer 1, OCR1A interrupt
                     }
                  cmd = NULL;
                  break;

        case 's':   // Controlled axis Stop.
                 if (AxisStatus & MOVE_FLAG)
                 {
                    if (AxisStatus & DIR_FLAG) // Axis moving reverse?
                         TargetCount = CurrentCount - T3;        
                    else   // The axis is moving forward.
                       TargetCount = CurrentCount + T3;        
                                                         
                    AxisStatus |= DEC_FLAG;
                    AxisStatus &= ~ACC_FLAG;
                 }    
                 cmd = NULL;
                 break;

        case 'h':   // Home the axis.
                  if (!(AxisStatus & MOVE_FLAG))
                 {
                     CurrentCount = 0;
                     TargetCount = 0;
                    AxisStatus |= (FIRST_HOME_FLAG | AT_HOME_FLAG | IN_POSITION_FLAG);
                 }
                 cmd = NULL;
                 break;

        case 'v':   // Set the axes velocity.
                  if (!(AxisStatus & MOVE_FLAG))
                  {
                    CommandVelocity = (unsigned int)atoi(GetData(COMMAND_DATA));
                   
                    // Limit the "Target Velocity" to a value of 100, "~55 IPM".
                    if (CommandVelocity < 100)
                         CommandVelocity = 100;
                   
                    SetVelocity();
                 }      
                 cmd = NULL;
                 break;
     }
  } while (1); // Repeat this loop forever!
}

void SetVelocity (void)
{
  TargetVelocity = CommandVelocity;

  if (AxisStatus & AXIS_DIR)
  {  // Reverse direction
     if (abs(TargetCount - CurrentCount) < (T1 + T3))
     {
          TargetVelocity = StartingVelocity - (CurrentCount - TargetCount) / 2;
          TriggerCount = TargetCount - (TargetCount - CurrentCount) / 2;
     }
     else TriggerCount = TargetCount + T3;
  }
  else
  {   // Forward direction
     if (abs(TargetCount - CurrentCount) < (T1 + T3))
        {
        TargetVelocity = StartingVelocity - (TargetCount - CurrentCount) / 2;
           TriggerCount = TargetCount - (TargetCount - CurrentCount) / 2;
     }
     else TriggerCount = TargetCount - T3;
  }    
}

/****************************************************************************/
// Retrieve a character string from the "Host" controller.
// This character string is in ASCII format.

char *GetData(char *d)
{
    int n = 0;
   char Temp = NULL;

   // Clear the character string buffer.
   memset(d, NULL, 10);

   do
   {    // Check for character, but don't wait!
#ifdef __iom16v_h
        if (UCSRA & (1<<RXC))
     {
        Temp = UDR;
#endif __iom16v_h

#ifdef __iom32v_h
        if (UCSRA & (1<<RXC))
     {
        Temp = UDR;
#endif __iom32v_h
         
#ifdef __iom64v_h        
        if (UCSR1A & (1<<RXC1))
     {
        Temp = UDR1;
#endif __iom64v_h
         
      // If the current character is not a "Carrage Return",
      // save the current character.
      if (Temp != '\r')
         d[n++] = Temp;
     }
     // Keep looping until the entire character string is retrieved.
   } while (Temp != '\r');

   return(d);
}

/****************************************************************************/    
// Timer 1 Output Compare Register 1A, OCR1A intrrupt vector.
#ifdef __iom16v_h
#pragma interrupt_handler SteppingIRQ:7
#endif __iom16v_h

#ifdef __iom32v_h
#pragma interrupt_handler SteppingIRQ:8
#endif __iom32v_h

#ifdef __iom64v_h
#pragma interrupt_handler SteppingIRQ:13
#endif __iom64v_h

// The rate of intrrupt is inversely proportional to the value in OCR1A.  That is,
// axis velocity is inversely proportional to current value stored in OCR1A.
// Large values cause the axis to go slow, small values cause the axis to go fast.

void SteppingIRQ(void)
{
     PORTC |= AXIS_STEP; // Turn the STEP output on.

     if (AxisStatus & DIR_FLAG)
     {  // The axis is moving in reverse, decrement axis position counter.
          if (--CurrentCount <= TriggerCount)    
          {
               AxisStatus |= DEC_FLAG;
              AxisStatus &= ~ACC_FLAG;
       }
     }
     else   // The axis is moving forward, increment axis position counter.
         if (++CurrentCount >= TriggerCount)    
           {
                 AxisStatus |= DEC_FLAG;
              AxisStatus &= ~ACC_FLAG;
        }    

     if (CurrentCount == TargetCount)
     {  // At target count, disable OCR1A intrrupt
          TIMSK &= ~(1<<OCIE1A);
       // Clear "MOVE_FLAG", "ACC_FLAG" & "DEC_FLAG".
       AxisStatus &= ~(MOVE_FLAG | ACC_FLAG | DEC_FLAG);
       // Set "IN_POSITION_FLAG".
       AxisStatus |= IN_POSITION_FLAG;
       
          if (CurrentCount == 0)
              // Axis is at "Home", set the "HOME_FLAG" bit.
              if (AxisStatus & FIRST_HOME_FLAG)
              AxisStatus |= AT_HOME_FLAG;
     }
     else
     {
              if (CurrentCount != 0)
           AxisStatus &= ~AT_HOME_FLAG;

        if (CurrentCount != TargetCount)
           AxisStatus &= ~IN_POSITION_FLAG;
                                     
           // Acceleration control.
           if (CommandVelocity < 500)
           if (AxisStatus & ACC_FLAG)
                     if (--OCR1A < TargetVelocity)
                         AxisStatus &= ~ACC_FLAG;
         
        // Deceleration control.
           if (CommandVelocity < 500)
           if (AxisStatus & DEC_FLAG)
                OCR1A ++;
     }

     PORTC &= ~AXIS_STEP; // Turn the STEP output off.
}

AVR_Admin- 04-23-2006

// Initialization file StepCon_INIT.c

CODE

#include <iom64v.h>
#include <macros.h>

// I/O Port initialization
void port_init(void)
{
PORTA = 0x00;
DDRA  = 0x00;
PORTB = 0x00;
DDRB  = 0x00;
PORTC = 0x00;
DDRC  = 0xE4;
PORTD = 0x00;
DDRD  = 0xE2;
}

// TIMER1 initializatio - prescale:1
void timer1_init(void)
{
TCCR1B = 0x00;        // Stop Timer Counter 1
TCNT1 = 0;           // Clear Timer Counter 1
OCR1A = 0x21;
OCR1B = 0x10;        // 10 miliseconds
TCCR1A = 0x00;        // Generate no outputs
TCCR1B = 0x0B;        // Start Timer Counter 1 with OCR1A, CLK / 1
}

// UART1 initialization
void uart1_init(void)
{
// For a 14.745600MHz crystal
// #define BAUD115_2K   7

// For a 16.000MHz crystal
// #define BAUD115_2K   8

// For a 21.855MHz crystal
#define BAUD115_2K   11

#ifdef __iom8535v_h
UCSRB = NULL; // Disable while setting baud rate
UCSRA = NULL; // 0x00;
UCSRC = 0x06; // 8 bit data
UBRRL = BAUD115_2K;    // Set baud rate lo
UBRRH = NULL;       // Set baud rate hi
UART_TRANSMIT_ON(); // Turn on the UART transmitter
UART_RECEIVE_ON(); // Turn on the UART receiver
#endif __iom8535v_h

#ifdef __iom32v_h
UCSRB = NULL; // Disable while setting baud rate
UCSRA = NULL; // 0x00;
UCSRC = 0x06; // 8 bit data
UBRRL = BAUD115_2K;    // Set baud rate lo
UBRRH = NULL;       // Set baud rate hi
UART_TRANSMIT_ON(); // Turn on the UART transmitter
UART_RECEIVE_ON(); // Turn on the UART receiver
#endif #ifdef __iom32v_h

#ifdef __iom64v_h
UCSR1B = NULL; // Disable while setting baud rate
UCSR1A = NULL; // 0x00;
UCSR1C = 0x06; // 8 bit data
UBRR1L = BAUD115_2K;    // Set baud rate lo
UBRR1H = NULL;       // Set baud rate hi

UART1_TRANSMIT_ON(); // Turn on the UART transmitter
UART1_RECEIVE_ON(); // Turn on the UART receiver
#endif __iom64v_h
}

// Initialize required controller peripherals
void init_devices(void)
{
CLI(); // Disable global interrupts
port_init();
timer1_init();
uart1_init();
       
MCUCR = NULL;
 
#ifdef __iom64v_h
EICRA = NULL;
EICRB = NULL;
EIMSK = NULL;
#endif
 
// Controller peripherals are now initialized
}  


AVR_Admin- 04-23-2006
// Modified getchar() function

CODE

#ifdef __iom8535_h
#include <iom8535v.h>
int getchar(void)
   {
   while ( !(UCSRA & (1<<RXC)) );      // RXC: USART Recieve Complete
       
   return UDR;
   }
#endif __iom8535_h
 
#ifdef __iom32_h
#include <iom32v.h>
int getchar(void)
   {
   while ( !(UCSRA & (1<<RXC)) );      // RXC: USART Recieve Complete
       
   return UDR;
   }
#endif __iom32v_h    

#ifdef __iom64_h
#include <iom64v.h>
int getchar(void)
   {
   while ( !(UCSR1A & (1<<RXC1)) );      // RXC: USART Recieve Complete
       
   return UDR1;
   }
#endif __iom64v_h    



// Modified putchar() function

CODE

extern int _textmode;

#ifdef __iom8535v_h
#include <iom8535v.h>
  int putchar(char c)
   {
   if (_textmode && c == '\n')
       putchar('\r');
   // Wait for empty transmit buffer
   while ( !(UCSRA & (1<<UDRE)) );
                         
   //Putting data into buffer , sends the data
   UDR = c;  
   return c;
   }
#endif __iom8535v_h    

#ifdef __iom32v_h
#include <iom32v.h>
int putchar(char c)
   {
   if (_textmode && c == '\n')
       putchar('\r');
   // Wait for empty transmit buffer
   while ( !(UCSRA & (1<<UDRE)) );
                         
   // Putting data into buffer , sends the data
   UDR = c;  
   return c;
   }
#endif __iom32v_h    

#ifdef __iom64v_h
#include <iom64v.h>
int putchar(char c)
   {
   if (_textmode && c == '\n')
       putchar('\r');
   /* Wait for empty transmit buffer */
   while ( !(UCSR1A & (1<<UDRE1)) );
                         
   /* Putting data into buffer , sends the data */
   UDR1 = c;  
   return c;
   }
#endif __iom64v_h  


Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.