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