FrequencyTimer2 - How to count pulses on FREQUENCYTIMER2_PIN for Stepper Motor?

While developing a Stepper Motor sketch for drilling circuit boards on a 3-Axis mill I came across the FrequencyTimer2 library. The "FrequencyTimer2::setPeriod(iPulseTime);" is a very simple way to set the Stepper Motor speed without any jitter. A short version of my code that shows how to control speed and direction of one motor with a JoyStick is included at the bottom of this post.

I am using ST-M5045 Microstep Drivers with NEMA23 Stepper Motors in my mill. The Driver is set to 1600 pulses per revolution of the Stepper Motor to provide the resolution needed. The drill file output from the PCB design software is simplified G-code and lists the X-Y coordinates of each drilled hole.

MY QUESTION IS: Where/how do I add a Pulse Count Variable in the FrequencyTimer2 library that increments by 1 each time a pulse is sent to the "FREQUENCYTIMER2_PIN"? Once I know where to add the counter I can add the functions to manage overflow and reset to Zero.

I need to count the number of pulses sent to the "FREQUENCYTIMER2_PIN" to calculate the exact X-Y distances moved when using the Joystick control.

I have studied the .cpp code and do not yet have enough AVR Timer and Register knowledge to understand where to add the Pulse Count variable into the library code. I do not need to use the Interrupt function and was unable to figure out how it was counting. I believe the Pulse Count must be maintained in the library to avoid timing issues with delays that may occur in the main Sketch loop.

Any help in this regard would be greatly appreciated!!!

I have tried using the various Stepper libraries unsuccessfully with the ST-M5045 Stepper Motor Driver. If someone knows of a work-around or way to make them functional with the larger stepper motor drivers that would save me a lot of time too.

// Stepper Motor Speed and Direction Control
// Using a Joystick and the FrequencyTimer2 library

// Microstep Driver ST-M5045, Steps per Revolution set to 1600

#include <FrequencyTimer2.h>

// Joy Stick variables
int pinJoy1 = A0; // Used for Stepper Motor X-Axis
int pinJoy2 = A1; // Used for Stepper Motor Y-Axis

int iJoy1 = 0;
int iJoy2 = 0;

// Stepper Motor Variables
int iPulseTime = 0;

int iDistTotal = 0;
int iDistNet = 0;

void setup() {

pinMode(8, OUTPUT); //direction pin Connected to DIR- on ST-M5045
digitalWrite(8, LOW);

pinMode(FREQUENCYTIMER2_PIN, OUTPUT); // Pin 11 on Uno board Connected to PUL- on ST-M5045

// ST-M5045 DIR+ and PUL+ Connected to Arduino 5VDC
}

void loop() {

iJoy1 = analogRead(pinJoy1); // Read the Joystick resistance value

// 509-510 is the neutral output reading of the Joystick I am using
// My full code includes a calibration routine in setup()

if (iJoy1 >= 510) {
iPulseTime = map(iJoy1,509,1023,10000,200); // Map resistance value to time interval
digitalWrite(8, HIGH); // Set the direction to Clockwise
FrequencyTimer2::setPeriod(iPulseTime); // Set the time interval for Timer2
FrequencyTimer2::enable(); // Turn on the Stepper Motor
}

else if (iJoy1 <= 495) {
iPulseTime = map(iJoy1,0,517,200,10000); // Map resistance value to time interval
digitalWrite(8,LOW); // Set the direction to Counter-Clockwise
FrequencyTimer2::setPeriod(iPulseTime); // Set the time interval for Timer2
FrequencyTimer2::enable(); // Turn on the Stepper Motor
}

else { // When the Joystick is in neutral, turn the Stepper Motor off
FrequencyTimer2::disable();
}
}

A link to the library you are referring to would make it easier to help you.

Thank you for the quick response. Here is a link to the library: FrequencyTimer2 Library

You can register a function to be called when the stepper is stepped, using FrequencyTimer2::setOnOverflow(). Then, in your function, increment a counter. Look at how Burp() is used in the example.

Thank you very much...Works like a charm. Sometimes it's too easy to over-think things.

Basic Features:

  • Moving the joystick up turns the stepper motor clockwise and increases the speed.
    Moving the joystick down turns the stepper counter-clockwise and increases the speed
    Returning the joystick to neutral stops the stepper motor and calculates the Axis coordinate in Pulses
    Variables are in place to add a function to calculate from pulses the distance from zero in millimeters and inches
    The Axis "Pulse Position" is accurately maintained as the screw is turned back and forth.
    A rotary switch which uses 3 pins is called out to set the active Axis (X or Y or Z or OFF)
    A 100 Pulse Per Rev Encoder ($16.00 on eBay eBay "Hand wheel Pulse Encoder 100PPR CNC Mill Router Manual Control") can be added along with a second rotary switch to move each axis a specific distance per encoder pulse

I hope this provides a good foundation for those developing Heavy Duty Stepper Motor controls.

If you have any suggestions for improvement I would appreciate your feedback.

Here is the Joystick operational portion the code:

/* Step Control
// Written by Benson Miller

Stepper Motor: Use any Stepper Motor compatible with the Microstep Driver

Driver: Microstep Driver ST-M5045
Pul+ to +5V
Pul- to Arduino Pin 11
Dir+ to +5V
Dir- to Arduino Pin 12
Enable+ not used
Enable- not used
I set it to 1,600 steps per revolution to achieve the distance resolution needed

Power Supply to Driver: 24VDC

Heavy Duty Stepper Motor Control
using Joy Stick to change speed and direction
Maintains accurate Axis coordinate position
*/

#include <FrequencyTimer2.h>

/// Switch pins and variables
int pinSwAxisReset = 1; // Momentary push button switch to set Axis coordinate to Zero
int pinSwX_AxisControl = 2; // Rotary Switch 1 sets which axis is reset and/or operated by the Rotary Encoder
int pinSwY_AxisControl = 3; // Rotary Switch 1
int pinSwZ_AxisControl = 4; // Rotary Switch 1
int pinSwMMorIN = 5; // Switch for Distance in millimeters or inches

bool bolX_AxisIsOn = false; // Used for tracking which Axis is On for use with Encoder
bool bolY_AxisIsOn = false; // Set when Rotary Switch 1 status is read each pass through the loop
bool bolZ_AxisIsOn = false;

// Encoder pins and variables
int pinPulseEncoder = 6 // Add a function to send pulses to active stepper motor
int pin SwDistance1 = 7; // Rotary Switch 2 sets distance (stepper pulses) to move for each encoder pulse
int pin SwDistance2 = 8; // Rotary Switch 2
int pin SwDistance3 = 9; // Rotary Switch 2
int pin SwDistance4 = 10; // Rotary Switch 2

int pinSpeedPot = A5; // Potentiometer sets the stepper pulse frequency (speed) for Encoder moves

int iPulsesToSend = 0; // Add a function to calculate stepper pulses to send base on Rotary Switch 2 setting

// Joystick pins and variables
int pinJoy1 = A0; // Used for Stepper Motor 1
int pinJoy2 = A1; // Used for Stepper Motor 2

int iJoy1 = 0;
int iJoy2 = 0;

// Stepper Motor Variables
// Change the constants as required to match your motor and screw pitch

const int iPULSES_PER_REV = 1600;
const double dDIST_MM_PER_SCREW_REV = 5;
const double dDIST_IN_PER_SCREW_REV = .19685;

double dMM_PerPulse = 0; // Stores millimeters moved per stepper pulse
double dIN_PerPulse = 0; // Stores inches moved per stepper pulse

int iPulseTime = 0; // microseconds for each stepper pulse
volatile unsigned long iPulseCount = 0; // Number of pulses the stepper motor is rotated
bool bolDirectionIsClockwise = NULL; // Tracks direction of rotation

long lX_AxisToPosition = 0; // Input for the next X-Axis coordinate
long lX_AxisPosition = 0; // Used to track X-Axis distance from coordinate system Origin

long lY_AxisToPositionl = 0; // Input for the next Y-Axis coordinate
long lY_AxisPosition = 0; // Used to track Y-Axis distance from coordinate system Origin

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void setup() {

// Calculate the distance traveled per step
dMM_PerPulse = dDIST_MM_PER_SCREW_REV / iPULSES_PER_REV;
dIN_PerPulse = dDIST_IN_PER_SCREW_REV / iPULSES_PER_REV;

pinMode(12, OUTPUT); //Stepper Motor Driver direction pin
digitalWrite(12, LOW);

// pinMode(11, OUTPUT); //step pin used for bit pounding the Stepper Motor Speed
// digitalWrite(11, LOW);

pinMode(FREQUENCYTIMER2_PIN, OUTPUT); // Pin 11 on Uno board

// setup Switch pins for input here

// Add Joystick neutral resistance calibration routine here to run one time

}
// END setup

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void loop() {

// Add code to read rotary switch position to determine active Axis

iJoy1 = analogRead(pinJoy1); // Read the Joystick resistance value for the X-Axis

if (iJoy1 >= 502) { // Use the value of neutral Joystick resistance +2
iPulseTime = map(iJoy1,502,1023,10000,200); // Convert Joystick resistance to pulse frequency
digitalWrite(8, HIGH); // Set stepper direction to Counter Clockwise
FrequencyTimer2::setPeriod(iPulseTime); // Set the pulse frequency
FrequencyTimer2::setOnOverflow(fnPulseCount); // Start counting pulses when FrequencyTimer2 is enabled
FrequencyTimer2::enable(); // Start turning the stepper motor
bolDirectionIsClockwise = false; // Indicates motor is turning Counter Clockwise
}
else if (iJoy1 < 500) { // Use the value of neutral Joystick resistance -2
iPulseTime = map(iJoy1,0,500,200,10000);
digitalWrite(8,LOW); // Set stepper direction to Clockwise
FrequencyTimer2::setPeriod(iPulseTime);
FrequencyTimer2::setOnOverflow(fnPulseCount);
FrequencyTimer2::enable();
bolDirectionIsClockwise = true; // Indicates motor is turning Clockwise
}
else { // Joystick has returned to neutral position so stop motor
FrequencyTimer2::setOnOverflow(0); // Turn off counting stepper pulses
FrequencyTimer2::disable(); // Turn off the stepper motor

noInterrupts(); // disable interrupts while reading the pulse count

// Replace If / Else If below with a function to calculate the active Axis position and distance traveled
if (bolDirectionIsClockwise == true) {
lX_AxisPosition = lX_AxisPosition + iPulseCount; // Add pulse count to previous Axis position
iPulseCount = 0; // Zero pulse count
}
else {
lX_AxisPosition = lX_AxisPosition - iPulseCount; // Subtract pulse count from previous position
iPulseCount = 0; // Zero pulse count
}

interrupts(); // Turn interrupts back on
bolDirectionIsClockwise = NULL; // NULL so Axis position math is skipped until Joystick is moved again

}

// For high-torque requirements add a function to compare the change in Joystick resistance and "Ramp" the change
// using a loop.
// Travel distance calculations can be added here
// Send X-Y-Z coordinates to LCD

}
// END LOOP

//**************************************************************
//**************************************************************
//**************************************************************
// Begin Function Definitions

void fnPulseCount() {
iPulseCount++; // Count pulses sent to Stepper Motor during movement by Joystick
}

//**************************************************************
void fnAxisPositionReset(char cIn) {
// Triggered by RESET push button and Rotary Switch 1 to zero selected Axis Pulse Coordinate at current position
if (cIn == 'X') {
lX_AxisPosition = 0;
}
else if (cIn == 'Y') {
lY_AxisPosition = 0;
}
else {
lZ_AxisPosition = 0;
}

PLEASE go back to your earlier posts and modify them to surround your code with code tags (the # button) so it looks like this. That makes it very much easier to read.

I had a quick look at the link you posted for the library. Am I correct in thinking it can only drive one stepper motor on a Uno?

...R

go930turbo:
Thank you very much...Works like a charm. Sometimes it's too easy to over-think things.

Basic Features:

  • Moving the joystick up turns the stepper motor clockwise and increases the speed.
    Moving the joystick down turns the stepper counter-clockwise and increases the speed
    Returning the joystick to neutral stops the stepper motor and calculates the Axis coordinate in Pulses
    Variables are in place to add a function to calculate from pulses the distance from zero in millimeters and inches
    The Axis "Pulse Position" is accurately maintained as the screw is turned back and forth.
    A rotary switch which uses 3 pins is called out to set the active Axis (X or Y or Z or OFF)
    A 100 Pulse Per Rev Encoder ($16.00 on eBay eBay "Hand wheel Pulse Encoder 100PPR CNC Mill Router Manual Control") can be added along with a second rotary switch to move each axis a specific distance per encoder pulse

I hope this provides a good foundation for those developing Heavy Duty Stepper Motor controls.

If you have any suggestions for improvement I would appreciate your feedback.

Here is the Joystick operational portion the code:

/* Step Control

// Written by Benson Miller

Stepper Motor: NEMA23, 4 wire,  0.9 degrees per step

Driver: Microstep Driver ST-M5045
     Pul+  to +5V
     Pul-  to Arduino Pin 11
     Dir+  to +5V
     Dir- to Arduino Pin 12
     Enable+ not used
     Enable- not used
     I set it to 1,600 steps per revolution to achieve the distance resolution needed

Power Supply to Driver: 24VDC

Heavy Duty Stepper Motor Control
             using Joy Stick to change speed and direction
Maintains accurate Axis coordinate position
*/

#include <FrequencyTimer2.h>

/// Switch pins and variables
int pinSwAxisReset = 1;            // Momentary push button switch to set Axis coordinate to Zero
int pinSwX_AxisControl = 2;        // Rotary Switch 1  sets which axis is reset and/or operated by the Rotary Encoder
int pinSwY_AxisControl = 3;        // Rotary Switch 1
int pinSwZ_AxisControl = 4;        // Rotary Switch 1
int pinSwMMorIN = 5;                   // Switch for Distance in millimeters or inches

bool bolX_AxisIsOn = false;       // Used for tracking which Axis is On for use with Encoder
bool bolY_AxisIsOn = false;                 //  Set when Rotary Switch 1 status is read each pass through the loop
bool bolZ_AxisIsOn = false;

// Encoder pins and variables
int pinPulseEncoder = 6          // Add a function to send pulses to active stepper motor
int pin SwDistance1 = 7;          // Rotary Switch 2 sets distance (stepper pulses) to move for each encoder pulse
int pin SwDistance2 = 8;         // Rotary Switch 2
int pin SwDistance3 = 9;         // Rotary Switch 2
int pin SwDistance4 = 10;       // Rotary Switch 2
       
         int pinSpeedPot = A5;             // Potentiometer sets the stepper pulse frequency (speed) for Encoder moves

int iPulsesToSend = 0;    // Add a function to calculate stepper pulses to send base on Rotary Switch 2 setting

// Joystick pins and variables
int pinJoy1 = A0;  // Used for Stepper Motor 1
int pinJoy2 = A1; // Used for Stepper Motor 2

int iJoy1 = 0;
int iJoy2 = 0;

// Stepper Motor Variables
     // Change the constants as required to match your motor and screw pitch

const int iPULSES_PER_REV = 1600;
const double dDIST_MM_PER_SCREW_REV = 5;
const double dDIST_IN_PER_SCREW_REV = .19685;

double dMM_PerPulse = 0;   // Stores millimeters moved per stepper pulse
double dIN_PerPulse = 0;     // Stores inches moved per stepper pulse

int iPulseTime = 0;                    // microseconds for each stepper pulse
volatile unsigned long  iPulseCount = 0;    // Number of pulses the stepper motor is rotated
bool bolDirectionIsClockwise = NULL;        // Tracks direction of rotation

long lX_AxisToPosition = 0;  // Input for the next X-Axis coordinate
long lX_AxisPosition = 0;  // Used to track X-Axis distance from coordinate system Origin

long lY_AxisToPositionl = 0;  // Input for the next Y-Axis coordinate
long lY_AxisPosition = 0;  // Used to track Y-Axis distance from coordinate system Origin

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void setup() {

// Calculate the distance traveled per step
dMM_PerPulse = dDIST_MM_PER_SCREW_REV / iPULSES_PER_REV;
dIN_PerPulse = dDIST_IN_PER_SCREW_REV / iPULSES_PER_REV;
               
      pinMode(12, OUTPUT);  //Stepper Motor Driver direction pin
      digitalWrite(12, LOW);

// pinMode(11, OUTPUT); //step pin used for bit pounding the Stepper Motor Speed
     // digitalWrite(11, LOW);
 
     pinMode(FREQUENCYTIMER2_PIN, OUTPUT);  // Pin 11 on Uno board

// setup Switch pins for input here

//  Add Joystick neutral resistance calibration routine here to run one time

}
// END setup

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void loop() {
 
  // Add code to read rotary switch position to determine active Axis

iJoy1 = analogRead(pinJoy1);                                                                // Read the Joystick resistance value for the X-Axis
 
  if (iJoy1 >= 502)  {           // Use the value of neutral Joystick resistance +2
  iPulseTime = map(iJoy1,502,1023,10000,200);    // Convert Joystick resistance to pulse frequency
digitalWrite(8, HIGH);                                                // Set stepper direction to Counter Clockwise
  FrequencyTimer2::setPeriod(iPulseTime);        // Set the pulse frequency
FrequencyTimer2::setOnOverflow(fnPulseCount);  // Start counting pulses when FrequencyTimer2 is enabled
  FrequencyTimer2::enable();                                   //  Start turning the stepper motor
bolDirectionIsClockwise = false;                           //  Indicates motor is turning Counter Clockwise
  }
  else if (iJoy1 < 500)  {          // Use the value of neutral Joystick resistance -2
  iPulseTime = map(iJoy1,0,500,200,10000);
digitalWrite(8,LOW);                                                 // Set stepper direction to Clockwise
  FrequencyTimer2::setPeriod(iPulseTime);
FrequencyTimer2::setOnOverflow(fnPulseCount);
  FrequencyTimer2::enable();
bolDirectionIsClockwise = true;                            //  Indicates motor is turning Clockwise
  }
  else  {                                                                                             // Joystick has returned to neutral position so stop motor
  FrequencyTimer2::setOnOverflow(0);                    // Turn off counting stepper pulses
  FrequencyTimer2::disable();                                        // Turn off the stepper motor

		  noInterrupts();                                                                 // disable interrupts while reading the pulse count

// Replace If / Else If below with a function to calculate the active Axis position and distance traveled
  if (bolDirectionIsClockwise == true)  {
  lX_AxisPosition = lX_AxisPosition + iPulseCount;   //  Add pulse count to previous Axis position
                                           iPulseCount = 0;                                           // Zero pulse count
  }
  else  {
  lX_AxisPosition = lX_AxisPosition - iPulseCount;     //  Subtract pulse count from previous position
                                          iPulseCount = 0;                                           // Zero pulse count
  }
 
  interrupts();                                                    // Turn interrupts back on
  bolDirectionIsClockwise = NULL;            //  NULL so Axis position math is skipped until Joystick is moved again

	  }

// For high-torque requirements add a function to compare the change in Joystick resistance and "Ramp" the change
                    //  using a loop.
          // Travel distance calculations can be added here
          //  Send X-Y-Z coordinates to LCD

}
// END LOOP

//**************************************************************
//**************************************************************
//**************************************************************
// Begin Function Definitions

void fnPulseCount() {
iPulseCount++;    // Count pulses sent to Stepper Motor during movement by Joystick
}

//**************************************************************
void fnAxisPositionReset(char cIn) {
// Triggered by RESET push button and Rotary Switch 1 to zero selected Axis Pulse Coordinate at current position
  if (cIn == 'X') {
  lX_AxisPosition = 0;
  }
  else  if (cIn == 'Y') {
  lY_AxisPosition = 0;
  }
          else {
           lZ_AxisPosition = 0;
}

To operate more than one stepper motor, a CMOS 4081(B) series Quad 2-Input AND Gate can be inserted between the Arduino and the Stepper Drivers. I have not yet tested the circuit but expect it to work OK for this application.

The Rotary Switch1 (Axis Selection) Pins should be LOW when OFF, and go HIGH when ON. Use a pull-down resistor on each Rotary Switch1 Pin.

LOGIC GATE OPTION
Input 1 for all of the Logic Gates is connected to Arduino Pin 11 (Stepper Pulse Pin).

Gate 1 Input 2 is attached to Pin 2 (pinSwX_AxisControl) // Rotary Switch 1 sets which axis is reset and/or operated
Gate 2 Input 2 is attached to Pin 3 (pinSwY_AxisControl) // Rotary Switch 1
Gate 3 Input 2 is attached to Pin 4 (pinSwZ_AxisControl) // Rotary Switch 1
Gate 4 - Unused or available for a fourth Axis

Gate 1 Output is attached to the X-Axis Stepper Motor Driver Pul-
Gate 2 Output is attached to the Y-Axis Stepper Motor Driver Pul-
Gate 3 Output is attached to the Z-Axis Stepper Motor Driver Pul-
Gate 4 - Unused or available for a fourth Axis

When Rotary Switch 1 is used to select a specific Axis for operation it sets the related Gate Input 2 HIGH which passes the stepper pulses through to the Stepper Driver. The output from the other gates remain LOW so no pulses will be sent to the other Stepper Drivers.

More Logic Gates could be added to manage additional Stepper Motors.

MOSFET OPTION
Another option (easier to understand) is to use an n-channel, enhancement Mosfet for each Axis. Connect each Rotary Switch1 Pin to a separate Mosfet Gate. Connect each Mosfet Drain to the respective Stepper Driver PUL-. Finally, connect the Source of all Mosfets to Pin 11. When an Axis is selected with Rotary Switch1, that Mosfet will turn on, allowing the pulses from Pin 11 to pass through only to the selected Stepper Driver.

For safety, I highly recommend adding normally closed limit switches to each axis along with an Emergency Stop switch. At a minimum, they can all be connected together in series as one normally closed loop to use only one Arduino Pin for input. Add a function that checks the status of the limit switches after each pulse is sent to a stepper motor. Call the function from within the fnPulseCount() function. If the limit switch loop opens, turn off the Motor(s). Crashing a heavy duty stepper motor is no fun and can easily break things.

Here is some code to manage the limit switches during Joystick operation:

// Add before Setup()
	int pinLimitSwitchLoop = 12;       // A normally closed loop of all limit switches with a Pull-Down Resistor
        bool bolLimtSwitchTriggered = false;  //  Set to true when a limit switch is opened 

// Add to Setup()
       pinMode(pinLimitSwitchLoop, INPUT);     // Add a Pull-Down resistor to the Limit Switches Normally Closed Loop
       digitalWrite(pinLimitSwitchLoop, HIGH); // When a Limit Switch is hit, this Pin will read LOW 

// Changes in Loop()

// REPLACE  "if (iJoy1 >= 502)  {"   WITH: " if (iJoy1 >= 502 && bolLimtSwitchTriggered == false)  {"
// REPLACE " else if (iJoy1 < 500)  {"  WITH: " else if (iJoy1 < 500 && bolLimtSwitchTriggered == false)  {

// Add the following function after the main loop()

 void fnLimitSwitchCheck(){
	 // Check Limit Switch Loop and turn off Motors if the Switch Loop is Open (LOW)
         // Call this function after each Pulse is sent to a Stepper Motor to avoid a machine crash

	 if (digitalRead(pinLimitSwitchLoop) == LOW)   // A limit switch has been opened.  Turn off the motor
	 {
		FrequencyTimer2::setOnOverflow(0);
		FrequencyTimer2::disable();
		bolLimtSwitchTriggered = true;  // A limit switch has been opened
                // You can turn on an LED here to indicate a Limit Switch Shutdown condition
	 }
	 else {
		bolLimtSwitchTriggered = false;  // The limit switch loop is properly closed so continue stepper movement
	 }
 }

// Add the following line at the end of fnPulseCount()
     fnLimitSwitchCheck();  // Check to see if a Limit Switch has been opened

The end product I am developing/designing has two basic purposes:

  1. Supplement an existing Mach3 Controller Board to provide precise "Manual" operation of the 5-Axis mill without having to use the Mach3 software. Basic surfacing operations, calibration, panel cut-outs, and other simple tasks can be done without taking the time to draw it out in CAD software. This sure beats a lot of knob turning.
  2. Automate the PCB drilling operations for boards I am making in small quantity pre-production runs.

The component cost should be under $50.00 if I don't get too carried away with bells and whistles.

I am designing the circuit board (Shield) now for my own use on a 3-Axis mill I am building and on a 5-Axis mill already in operation. If anyone is interested in the board layout or in purchasing some boards, please let me know.
go930turbo@gmail.com

Stepper Motor: NEMA23, 4 wire, 0.9 degrees per step

You should google NEMA23. NEMA is a standard. The value, 23, describes the mounting hole location and size information. Hardly useful information for us. You might as well tell us what color it is, for all the usefulness of the information you are providing.

Now, voltage and current are a different (omitted) story.

go930turbo:
To operate more than one stepper motor, a CMOS 4073(B) Triple 3-Input AND Gate is required. I have not yet tested the circuit but expect it to work OK for this application.

The Rotary Switch 1 (Axis Selection) Pins should be LOW when OFF, and go HIGH when ON.

I think you have in mind just moving one motor at a time rather than moving two or three at the same time in response to G Code.

If you are just using the Arduino to give additional manual postion control to some steppers I would have thought simple code using digitalWrite() and millis() or micros() for timing would be sufficient. And it would probably avoid the need for external logic gates.

...R

I agree that the following function (called each pass through the main loop()) may be easier to write and execute. I started testing using code similar to the following: (This could be run directly in the loop() rather than in a function)

 bool fn_bolMoveStepper1(int iAxis1Pin, int iFrequency, bool bolDirClockwise, long lStepsToMove){
	long lSteps1Moved; 
	// Call this function after reading the Joystick resistance value and Axis selection switches in the main loop()
	//        When called by Joystick movement set lStepsToMove = 0
       //  OR, use the Speed Pot to get the Frequency and calculate the steps to move and direction 
       //            based on simple math against the coordinate system

	if (bolDirClockwise == true) {
		digitalWrite(8,HIGH);  // Set Stepper rotation to Clockwise
	} 
	else {
		digitalWrite(8,LOW);  // Set Stepper rotation to Counter-clockwise
	}
	
	if (iFrequency !=0 && lSteps1Moved <= lStepsToMove)	{	
		digitalWrite(iAxis1Pin, HIGH);
		delayMicroseconds(iPulseTime);
		digitalWrite(iAxisPin, LOW);
		delayMicroseconds(iPulseTime);
	}
	
	fnPulseCount();   // Count pulses sent to Stepper and check limit switches
	
	if (lStepsToMove > 0) {
		lSteps1Moved++;
		
		if (lSteps1Moved > lStepsToMove) {
			lSteps1Moved = 0;
			return true;  // The steps to move have been completed
		}
	}
	


 }
	fnPulseCount();   // Count pulses sent to Stepper and check limit switches
        // Insert code to update the coordinate system position array 
        return false;  // There are still steps to move
 }

Substituting elapsedMillis for the delays would avoid the inherent problems of using delays in the loop(). As I added the complexity of reading the next coordinate position, calculating the required steps, executing the moves and writing coordinate positions to an LCD, jitter was introduced. The problem gets worse if the pulses per revolution is set above 3200 in the Driver. Doubling the PPR requires doubling the step pulse frequency to maintain the same stepper motor speed. Above 3200 PPR the pulse on/off time can get below 20 microseconds to run a motor at full speed.

I am running the X and Y motors simultaneously both with the Joystick and for X/Y coordinate changes. I did not include all of the code because it can be confusing for someone trying to figure out the basics of controlling a high power stepper driver.

Putting each stepper on a separate Arduino Pin requires two digitalWrite operations (I haven't learned how to manipulate the registers directly yet to set both pins in one cycle.) Using one pulse pin along with a Mosfet for each motor (speed controlled by the Pot Pin reading) makes it easy to count steps and turn off each Axis after its required steps have been moved. The pulse frequency stays rock solid providing very smooth movement along each axis which is more important in milling operations than in drilling operations.

I don't have the time right now to dig into the direct manipulation of the Arduino output pins through the registers. If you want to provide a code snippet and truth table to do so I would be glad to experiment with it and compare the results.

Finally, I do agree that for a basic drilling mill, using the FrequencyTimer2 library is not necessary and the code I provided in fn_bolMoveStepper1 could be moved to and run in the main loop().

I am still in a steep learning curve with AVR hardware/embedded programming and really appreciate your comments and suggestions. There are often many different ways to accomplish a given task and I enjoy learning new thoughts and ideas.

go930turbo:
The pulse frequency stays rock solid

How are you measuring this and what is the pulse frequency?

...R