Hello, everyone! This is my first post, so first off: thank you to everyone around this community for all of the help you've unknowingly provided to me over the years ![]()
Now to my issue. I'm writing a class for a stepper motor driver, and I'm using the arduino timers (Timer 3) to control the step pulses to the motor driver. The essence of what I'm going for is the following:
-
Stepper Speed Ramp Class: contains speed ramp information like max speed, acceleration, deceleration, an array for OCR values, and a couple member functions for setting max speed and calculating the speed ramp (OCR values in the array).
-
CL42T Class: this class inherits (publicly) the Stepper Speed Ramp class, and contains timer register pointers, I/O port register pointers, timer masks, and a few other member variables. This class sets the pointers to the appropriate addresses in the constructor, and has member functions for SetupDrive() (set the timer control registers, clear count, etc.), Enable/DisableDrive (set enable pin high/low), SetDirection(char) (set direction pin high or low), run/stop (start/stop timer), and Increment (set target steps, turn on timer interrupt, call RunDrive()).
-
I plan on writing more classes which will inherit CL42T and will have subassembly context member functions (i.e. Initialize, Jog, etc.)
To test this class structure, I've written a sketch to test run the stepper motor for one revolution (of the subassembly), increase the speed, and repeat. Unfortunately, I am experiencing very odd behavior out of the timer during the first loop of my sketch (as well as the fourth loop) where The timer seems to be at a much higher frequency than expected, thus running the motor at some awful speed and not the currently set "maxSpeed". Loops 2 and 3, as well as up to 10 have shown the expected behavior, but I have not gone beyond 10 to look for any deeper patterns.
I know that the init() function adjusts the timer registers before setup, so I clear the control registers for timers 1 through 5 in setup(). I want to potentially use micros() and/or millis() for this application, so I'm steering clear of messing with Timer 0. While reading through the datasheet to find potential causes to my issue, I noticed that the USART0 interrupts have a higher priority than the Timer 3 interrupts, so I suspected maybe something with the serial dumps in my test were interfering with the timer output compare interrupt. Unfortunately, with all of the serial removed from my sketch, the incorrect behavior remains. I've also tried (total shotgun move here) "synchronizing" the prescale for Timers 1,3,4,5 (GTCCR |= 1;), but I don't think I fully grasp exactly what this does, and it did not resolve the issue.
After all of my troubleshooting, I've stayed consistent with two potential root causes (although I can't seem to locate the smoking gun for either):
-
Through the class structure, I'm setting timer control registers in an incorrect order or something of the sort. I've written many successful test sketches directly manipulating the timer control registers to get a square wave output, but I've never wrapped them in a class like this. Perhaps there is something out of order that I'm simply not seeing. Even with serial printing all of the register values (directly - not using my class structure), I cannot see anything wrong with the settings just before starting the timer/counter.
-
Some sort of overflow or other calculation / data type error. I've looked through this, and looked up all of the C++ standard promotions, but I can't seem to find anywhere that would cause a problem in the CalculateSpeedRamp() member function. Furthermore, I've printed out the OCR array values and confirmed them against manual calculations - no issues found. While some of the values are quite large, I'm using large data types, and the resulting OCR values seem to match up to my manual calculations. Directly accessing the OCR values just prior to starting the movement also confirms the correct settings.
I had originally thought that the Timer0 interrupts might be taking excessive time and interfering with my speed ramp, but I no longer think that is the case. For one, I set and confirm the OCR value at multiple points prior to starting the movement. If the interrupt were being interfered with (blocked), then my drive would not speed up and would stay at the initial OCR value. However, since my drive is going way too fast during the "error loops", that cannot be the cause. Somehow, either the prescale bits in the timer are not being set properly, thus leading to a higher frequency count, or the OCR registers are not being written properly. Either one of these I have not been able to find as the serial printouts confirm desired settings.
Can anyone please help find where I've gone wrong? @nickgammon , parhaps?
I'm embedding the code below, and I'll attach my serial data output. Thanks in advance!
EDIT: I'm using VS Code 1.62.0 with Arduino Extension 0.4.7 and C/C++ 1.7.1
CL42TClassTest.ino:
#include "CL42T.h"
// prescale value
int PreScale = 8;
// Step Counter
volatile unsigned long TestStepperSteps = 0;
bool oneShot = true;
const char TestStepperStepPort = 'E';
const int TestStepperStepBit = 3;
const int TestStepperControlBit = 2;
const int TestStepperTimerNum = 3;
const char TestStepperTimerOutput = 'A';
const int TestStepperOneRevSteps = 27774;
// create test stepper object (global object)
CL42T TestStepperDriveObject(TestStepperStepPort,TestStepperStepBit,TestStepperControlBit,TestStepperTimerNum,TestStepperTimerOutput,PreScale);
// Macros for anscillary equipment
#define VDC24_Setup DDRF |= B00010000 // set 24VDC Control Relay pin to OUTPUT
#define VDC24_On PORTF |= B00010000 // set 24VDC Control Relay pin HIGH
#define VDC24_Off PORTF &= B11101111 // set 24VDC Control Relay pin LOW
// max speed variable
int MAXspeed = 60;
void setup() {
Serial.begin(115200); // start serial at 115200 BAUD
while (!Serial) {
; // wait for serial to connect
}
// header
Serial.println(F("Program to test CL42T Class."));
// clear timer registers and print verification
clearTimersOneToFive();
Serial.println(F("Timers 1-5 Register Values After Reset:"));
Serial.print(F("TCCR0A Value: "));prntByteBIN(TCCR0A);
Serial.print(F("TCCR0B Value: "));prntByteBIN(TCCR0B);
Serial.print(F("TCNT0 Value: "));Serial.println(TCNT0,HEX);
Serial.print(F("OCR0A Value: "));Serial.println(OCR0A,HEX);
Serial.print(F("TIMSK0 Value: "));prntByteBIN(TIMSK0);
Serial.print(F("TIFR0 Value: "));prntByteBIN(TIFR0);
Serial.print(F("TCCR1A Value: "));prntByteBIN(TCCR1A);
Serial.print(F("TCCR1B Value: "));prntByteBIN(TCCR1B);
Serial.print(F("TCCR1C Value: "));prntByteBIN(TCCR1C);
Serial.print(F("TCNT1 Value: "));Serial.println(TCNT1,HEX);
Serial.print(F("OCR1A Value: "));Serial.println(OCR1A,HEX);
Serial.print(F("TIMSK1 Value: "));prntByteBIN(TIMSK1);
Serial.print(F("TIFR1 Value: "));prntByteBIN(TIFR1);
Serial.print(F("TCCR2A Value: "));prntByteBIN(TCCR2A);
Serial.print(F("TCCR2B Value: "));prntByteBIN(TCCR2B);
Serial.print(F("TCNT2 Value: "));Serial.println(TCNT2,HEX);
Serial.print(F("OCR2A Value: "));Serial.println(OCR2A,HEX);
Serial.print(F("TIMSK2 Value: "));prntByteBIN(TIMSK2);
Serial.print(F("TIFR2 Value: "));prntByteBIN(TIFR2);
Serial.print(F("TCCR3A Value: "));prntByteBIN(TCCR3A);
Serial.print(F("TCCR3B Value: "));prntByteBIN(TCCR3B);
Serial.print(F("TCCR3C Value: "));prntByteBIN(TCCR3C);
Serial.print(F("TCNT3 Value: "));Serial.println(TCNT3,HEX);
Serial.print(F("OCR3A Value: "));Serial.println(OCR3A,HEX);
Serial.print(F("TIMSK3 Value: "));prntByteBIN(TIMSK3);
Serial.print(F("TIFR3 Value: "));prntByteBIN(TIFR3);
Serial.print(F("TCCR4A Value: "));prntByteBIN(TCCR4A);
Serial.print(F("TCCR4B Value: "));prntByteBIN(TCCR4B);
Serial.print(F("TCCR4C Value: "));prntByteBIN(TCCR4C);
Serial.print(F("TCNT4 Value: "));Serial.println(TCNT4,HEX);
Serial.print(F("OCR4A Value: "));Serial.println(OCR4A,HEX);
Serial.print(F("TIMSK4 Value: "));prntByteBIN(TIMSK4);
Serial.print(F("TIFR4 Value: "));prntByteBIN(TIFR4);
Serial.print(F("TCCR5A Value: "));prntByteBIN(TCCR5A);
Serial.print(F("TCCR5B Value: "));prntByteBIN(TCCR5B);
Serial.print(F("TCCR5C Value: "));prntByteBIN(TCCR5C);
Serial.print(F("TCNT5 Value: "));Serial.println(TCNT5,HEX);
Serial.print(F("OCR5A Value: "));Serial.println(OCR5A,HEX);
Serial.print(F("TIMSK5 Value: "));prntByteBIN(TIMSK5);
Serial.print(F("TIFR5 Value: "));prntByteBIN(TIFR5);
// setup 24VDC relay and ensure OFF
Serial.print(F("Setting up 24VDC control relay..."));
VDC24_Setup;
VDC24_Off;
Serial.println(F("Done."));
// setup stepper drive object
Serial.print(F("Setting up Test Stepper drive object..."));
TestStepperDriveObject.SetupDrive();
Serial.println(F("Done."));
// Timer settings printout
Serial.println(F("Setup Timer Settings: "));
Serial.print(F("OCR3A Value: "));Serial.println(OCR3A);
Serial.print(F("TCNT3 Value: "));Serial.println(TCNT3);;
Serial.print(F("TCCR3A Value: "));prntByteBIN(TCCR3A);
Serial.print(F("TCCR3B Value: "));prntByteBIN(TCCR3B);
Serial.print(F("TCCR3C Value: "));prntByteBIN(TCCR3C);
Serial.print(F("TIMSK3 Value: "));prntByteBIN(TIMSK3);
Serial.print(F("TIFR3 Value: "));prntByteBIN(TIFR3);
// Step pin port values printout
Serial.println(F("Setup DDRE and PORTE: "));
Serial.print(F("DDRE = "));prntByteBIN(DDRE);
Serial.print(F("PORTE = "));prntByteBIN(PINE);
// indication for main loop start
Serial.println(F("Now starting main loop..."));
delay(1000);
}
void loop()
{
if (oneShot)
{
// Timer settings printout
Serial.println(F("First Loop Timer Settings:"));
Serial.print(F("TCCR0A Value: "));prntByteBIN(TCCR0A);
Serial.print(F("TCCR0B Value: "));prntByteBIN(TCCR0B);
Serial.print(F("TCNT0 Value: "));Serial.println(TCNT0,HEX);
Serial.print(F("OCR0A Value: "));Serial.println(OCR0A,HEX);
Serial.print(F("TIMSK0 Value: "));prntByteBIN(TIMSK0);
Serial.print(F("TIFR0 Value: "));prntByteBIN(TIFR0);
Serial.print(F("TCCR1A Value: "));prntByteBIN(TCCR1A);
Serial.print(F("TCCR1B Value: "));prntByteBIN(TCCR1B);
Serial.print(F("TCCR1C Value: "));prntByteBIN(TCCR1C);
Serial.print(F("TCNT1 Value: "));Serial.println(TCNT1,HEX);
Serial.print(F("OCR1A Value: "));Serial.println(OCR1A,HEX);
Serial.print(F("TIMSK1 Value: "));prntByteBIN(TIMSK1);
Serial.print(F("TIFR1 Value: "));prntByteBIN(TIFR1);
Serial.print(F("TCCR2A Value: "));prntByteBIN(TCCR2A);
Serial.print(F("TCCR2B Value: "));prntByteBIN(TCCR2B);
Serial.print(F("TCNT2 Value: "));Serial.println(TCNT2,HEX);
Serial.print(F("OCR2A Value: "));Serial.println(OCR2A,HEX);
Serial.print(F("TIMSK2 Value: "));prntByteBIN(TIMSK2);
Serial.print(F("TIFR2 Value: "));prntByteBIN(TIFR2);
Serial.print(F("TCCR3A Value: "));prntByteBIN(TCCR3A);
Serial.print(F("TCCR3B Value: "));prntByteBIN(TCCR3B);
Serial.print(F("TCCR3C Value: "));prntByteBIN(TCCR3C);
Serial.print(F("TCNT3 Value: "));Serial.println(TCNT3,HEX);
Serial.print(F("OCR3A Value: "));Serial.println(OCR3A,HEX);
Serial.print(F("TIMSK3 Value: "));prntByteBIN(TIMSK3);
Serial.print(F("TIFR3 Value: "));prntByteBIN(TIFR3);
Serial.print(F("TCCR4A Value: "));prntByteBIN(TCCR4A);
Serial.print(F("TCCR4B Value: "));prntByteBIN(TCCR4B);
Serial.print(F("TCCR4C Value: "));prntByteBIN(TCCR4C);
Serial.print(F("TCNT4 Value: "));Serial.println(TCNT4,HEX);
Serial.print(F("OCR4A Value: "));Serial.println(OCR4A,HEX);
Serial.print(F("TIMSK4 Value: "));prntByteBIN(TIMSK4);
Serial.print(F("TIFR4 Value: "));prntByteBIN(TIFR4);
Serial.print(F("TCCR5A Value: "));prntByteBIN(TCCR5A);
Serial.print(F("TCCR5B Value: "));prntByteBIN(TCCR5B);
Serial.print(F("TCCR5C Value: "));prntByteBIN(TCCR5C);
Serial.print(F("TCNT5 Value: "));Serial.println(TCNT5,HEX);
Serial.print(F("OCR5A Value: "));Serial.println(OCR5A,HEX);
Serial.print(F("TIMSK5 Value: "));prntByteBIN(TIMSK5);
Serial.print(F("TIFR5 Value: "));prntByteBIN(TIFR5);
// Step pin port values printout
Serial.println(F("First Loop DDRE and PORTE:"));
Serial.print(F("DDRE = "));prntByteBIN(DDRE);
Serial.print(F("PORTE = "));prntByteBIN(PINE);
// CL42T class settings
Serial.println(F("C42T Class Settings: "));
Serial.print(F("directionPORT Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.directionPORT,HEX);
Serial.print(F("directionDDR Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.directionDDR,HEX);
Serial.print(F("directionPIN Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.directionPIN,HEX);
Serial.print(F("enablePORT Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.enablePORT,HEX);
Serial.print(F("enableDDR Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.enableDDR,HEX);
Serial.print(F("enablePIN Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.enablePIN,HEX);
Serial.print(F("alarmPORT Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.alarmPORT,HEX);
Serial.print(F("alarmDDR Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.alarmDDR,HEX);
Serial.print(F("alarmPIN Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.alarmPIN,HEX);
Serial.print(F("stepPORT Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepPORT,HEX);
Serial.print(F("stepDDR Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepDDR,HEX);
Serial.print(F("stepPIN Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepPIN,HEX);
Serial.print(F("stepTCCRA Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTCCRA,HEX);
Serial.print(F("stepTCCRA Value: "));prntByteBIN(*TestStepperDriveObject.stepTCCRA);
Serial.print(F("stepTCCRAmask: "));prntByteBIN(TestStepperDriveObject.stepTCCRAmask);
Serial.print(F("stepTCCRAOutput1Mask: "));prntByteBIN(TestStepperDriveObject.stepTCCRAOutput1Mask);
Serial.print(F("stepTCCRB Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTCCRB,HEX);
Serial.print(F("stepTCCRB Value: "));prntByteBIN(*TestStepperDriveObject.stepTCCRB);
Serial.print(F("stepTCCRBmask: "));prntByteBIN(TestStepperDriveObject.stepTCCRBmask);
Serial.print(F("stepTCCRBstartMask: "));prntByteBIN(TestStepperDriveObject.stepTCCRBstartMask);
Serial.print(F("stepTCCRC Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTCCRC,HEX);
Serial.print(F("stepTCCRC Value: "));prntByteBIN(*TestStepperDriveObject.stepTCCRC);
Serial.print(F("stepTIMSK Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTIMSK,HEX);
Serial.print(F("stepTIMSK Value: "));prntByteBIN(*TestStepperDriveObject.stepTIMSK);
Serial.print(F("stepTimerOutputInterrupt1Mask: "));prntByteBIN(TestStepperDriveObject.stepTimerOutputInterrupt1Mask);
Serial.print(F("stepTIFR Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTIFR,HEX);
Serial.print(F("stepTIFR Value: "));prntByteBIN(*TestStepperDriveObject.stepTIFR);
Serial.print(F("stepTCNTL Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTCNTL,HEX);
Serial.print(F("stepTCNTL Value: 0x"));Serial.println(*TestStepperDriveObject.stepTCNTL,HEX);
Serial.print(F("stepTCNTH Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepTCNTH,HEX);
Serial.print(F("stepTCNTH Value: 0x"));Serial.println(*TestStepperDriveObject.stepTCNTH,HEX);
Serial.print(F("stepOCRL Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepOCRL,HEX);
Serial.print(F("stepOCRL Value: 0x"));Serial.println(*TestStepperDriveObject.stepOCRL,HEX);
Serial.print(F("stepOCRH Address: 0x"));Serial.println((uint16_t)TestStepperDriveObject.stepOCRH,HEX);
Serial.print(F("stepOCRH Value: 0x"));Serial.println(*TestStepperDriveObject.stepOCRH,HEX);
Serial.print(F("OCRo: "));Serial.println(TestStepperDriveObject.OCRo);
// StepperSpeedRamp class settings
Serial.println(F("StepperSpeedRamp Class Settings:"));
Serial.print(F("maxSpeed: "));Serial.println(TestStepperDriveObject.maxSpeed);
Serial.print(F("Accel: "));Serial.println(TestStepperDriveObject.Accel);
Serial.print(F("Decel: "));Serial.println(TestStepperDriveObject.Decel);
Serial.print(F("mmPerStep: "));Serial.println(TestStepperDriveObject.mmPerStep);
Serial.print(F("preScale: "));Serial.println(TestStepperDriveObject.preScale);
Serial.print(F("CntFreq: "));Serial.println(TestStepperDriveObject.CntFreq);
Serial.print(F("speedUpdateSteps: "));Serial.println(TestStepperDriveObject.speedUpdateSteps);
Serial.print(F("maxSpeedSteps: "));Serial.println(TestStepperDriveObject.maxSpeedSteps);
Serial.print(F("maxAccelSteps: "));Serial.println(TestStepperDriveObject.maxAccelSteps);
Serial.print(F("accelSteps: "));Serial.println(TestStepperDriveObject.accelSteps);
Serial.print(F("decelSteps: "));Serial.println(TestStepperDriveObject.decelSteps);
Serial.print(F("numStepsTemp: "));Serial.println(TestStepperDriveObject.numStepsTemp);
Serial.print(F("OCRmin: "));Serial.println(TestStepperDriveObject.OCRmin);
Serial.print(F("OCRo: "));Serial.println(TestStepperDriveObject.OCRo);
Serial.print(F("accelDecelIndex: "));Serial.println(TestStepperDriveObject.accelDecelIndex);
Serial.println(F("OCR Array:"));
for (int i = 0; i < (sizeof(TestStepperDriveObject.OCRarray)/2); i++)
{
Serial.print(F("OCR "));Serial.print(i);Serial.print(F(" = "));Serial.println(TestStepperDriveObject.OCRarray[i]);
}
oneShot = false;
}
// set maxSpeed of drive & calculate OCR array, then apply
TestStepperDriveObject.SetMaxSpeed(MAXspeed);
TestStepperDriveObject.ApplyNewSpeed();
// turn on 24 VDC
Serial.print(F("Turning on 24VDC Control Relay..."));
VDC24_On;
Serial.println(F("Done."));
delay(1000);
// enable drive
Serial.print(F("Enabling Test Stepper Drive..."));
TestStepperDriveObject.EnableDrive();
Serial.println(F("Done."));
delayMicroseconds(500);
// set drive direction
Serial.print(F("Setting Test Stepper Direction to FWD..."));
TestStepperDriveObject.SetDirection('F');
Serial.println(F("Done."));
delayMicroseconds(500);
// move stepper drive one revolution
Serial.print(F("Starting Test Stepper at "));Serial.print(MAXspeed);Serial.println(F(" mm/s for one revolution..."));
TestStepperDriveObject.Increment(TestStepperOneRevSteps);
// monitor for end of movement
while (TestStepperSteps < TestStepperDriveObject.incrementSteps)
{
Serial.print(F("Steps taken: "));Serial.println(TestStepperSteps);
delay(250);
}
Serial.println(F("Done."));
// clear step tracker
TestStepperSteps = 0;
delayMicroseconds(500);
// disable stepper drive
Serial.print(F("Disabling Test Stepper Drive..."));
TestStepperDriveObject.DisableDrive();
Serial.println(F("Done."));
delayMicroseconds(500);
// turn off 24VDC
Serial.print(F("Turning off 24VDC Control Relay..."));
VDC24_Off;
Serial.println(F("Done."));
// adjust max speed up 20 mm/s
MAXspeed += 20;
// delay to slow test progression
delay(3000);
}
void prntByteBIN(byte b)
{
for(int i = 7; i >= 0; i--)
{
Serial.print(bitRead(b,i));
}
Serial.println();
}
void clearTimersOneToFive()
{
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TCCR1C = 0;
TIMSK1 = 0;
TCNT1H = 0;
TCNT1L = 0;
TIFR1 |= B00001111;
TCCR2A = 0;
TCCR2B = 0;
TIMSK2 = 0;
TCNT2 = 0;
TIFR2 |= B00001111;
TCCR3A = 0;
TCCR3B = 0;
TCCR3C = 0;
TIMSK3 = 0;
TCNT3H = 0;
TCNT3L = 0;
TIFR3 |= B00001111;
TCCR4A = 0;
TCCR4B = 0;
TCCR4C = 0;
TIMSK4 = 0;
TCNT4H = 0;
TCNT4L = 0;
TIFR4 |= B00001111;
TCCR5A = 0;
TCCR5B = 0;
TCCR5C = 0;
TIMSK5 = 0;
TCNT5H = 0;
TCNT5L = 0;
TIFR5 |= B00001111;
interrupts();
}
// Test Stepper Step Interrupt
ISR(TIMER3_COMPA_vect) {
if (((*TestStepperDriveObject.stepPIN) & (1<<TestStepperDriveObject.stepBIT))) {
TestStepperSteps++;
if ((TestStepperDriveObject.incrementSteps > 0) && (TestStepperSteps >= TestStepperDriveObject.incrementSteps))
{
TestStepperDriveObject.StopDrive(); // position reached, stop drive
// TestStepperSteps = 0;
}
if (TestStepperSteps <= TestStepperDriveObject.accelSteps)
{ // acceleration phase
if (!(TestStepperSteps % TestStepperDriveObject.speedUpdateSteps))
{
TestStepperDriveObject.accelDecelIndex++;
*TestStepperDriveObject.stepOCRH = highByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
*TestStepperDriveObject.stepOCRL = lowByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
}
}
if ((TestStepperDriveObject.incrementSteps > 0) && (TestStepperSteps >= (TestStepperDriveObject.incrementSteps - TestStepperDriveObject.decelSteps)))
{ // deceleration phase - position tracking only
if (!(TestStepperSteps % TestStepperDriveObject.speedUpdateSteps))
{
*TestStepperDriveObject.stepOCRH = highByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
*TestStepperDriveObject.stepOCRL = lowByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
TestStepperDriveObject.accelDecelIndex--;
}
}
}
}
StepperSpeedRamp.h:
#ifndef StepperSpeedRamp_h
#define StepperSpeedRamp_h
#include "Arduino.h"
// Stepper Speed Ramp Class for CL42T drives
class StepperSpeedRamp
{
public:
// drive settings
double maxSpeed; // mm/s
// variables for speed ramp calculations
double Accel; // mm/s/s
double Decel; // mm/s/s
double mmPerStep; // mm/step
int preScale; // prescale selection
long CntFreq; // counter frequency
int speedUpdateSteps; // # steps
long maxSpeedSteps; // # steps
long maxAccelSteps; // # steps
long accelSteps; // # steps
long decelSteps; // # steps
long numStepsTemp; // # steps
long OCRmin;
long OCRo;
// speed update index (OCRarray[])
volatile int accelDecelIndex;
// speed ramp OCR values
volatile uint16_t OCRarray[21];
// member functions
StepperSpeedRamp(int PreScale);
void SetMaxSpeed(int MmPerSec);
void CalculateSpeedRamp();
};
#endif
StepperSpeedRamp.cpp:
#include "Arduino.h"
#include "math.h"
#include "StepperSpeedRamp.h"
/*
* Stepper Speed Ramp Class
*/
StepperSpeedRamp::StepperSpeedRamp(int PreScale)
{
// drive settings
maxSpeed = 60; // mm/s - default @ 60
// variables for speed ramp calculations
Accel = maxSpeed*10; // mm/s/s - accelerate in 100 ms
Decel = maxSpeed*10; // mm/s/s - decelerate in 100 ms
mmPerStep = 0.04; // mm/step
preScale = PreScale; // copy prescale value into object
CntFreq = 16000000/preScale; // calculate counter frequency at PreScale
speedUpdateSteps = 0; // # steps
maxSpeedSteps = 0; // # steps
maxAccelSteps = 0; // # steps
accelSteps = 0; // # steps
decelSteps = 0; // # steps
numStepsTemp = 10000; // use 10,000 steps as the calculation for speed ramp
OCRmin = 0; // minimum delay --> max speed
OCRo = 0; // first delay --> start speed (based on acceleration)
accelDecelIndex = 0; // speed update index (OCRarray[])
CalculateSpeedRamp(); // calculate OCRarray
}
void StepperSpeedRamp::SetMaxSpeed(int MmPerSec)
{
maxSpeed = MmPerSec; // set new maxSpeed value
Accel = maxSpeed*10; // accelerate in 100 ms
Decel = maxSpeed*10; // decelerate in 100 ms
CalculateSpeedRamp(); // calculate new OCRarray
}
void StepperSpeedRamp::CalculateSpeedRamp()
{
OCRmin = ((mmPerStep*(CntFreq/2))/(maxSpeed)); // calculate minimum OCR - based on max speed
OCRo = ((CntFreq/2)*sqrt((2*mmPerStep)/Accel)); // calculate first OCR value - based on accel
OCRarray[0] = OCRo; // load first OCR value into OCRarray
maxSpeedSteps = ((maxSpeed*maxSpeed)/(2*mmPerStep*Accel)); // calculate steps required to reach max speed
maxAccelSteps = ((numStepsTemp*Decel)/(Accel+Decel)); // calculate steps required to reach max deceleration phase
if (maxSpeedSteps < maxAccelSteps)
{ // trapezoidal speed profile
accelSteps = maxSpeedSteps;
decelSteps = (accelSteps*(Accel/Decel));
}
else if (maxSpeedSteps >= maxAccelSteps)
{ // triangular speed profile
accelSteps = maxAccelSteps;
decelSteps = (accelSteps*(Accel/Decel));
}
speedUpdateSteps = accelSteps / ((sizeof(OCRarray)/2)-2); // 0 indexed AND OCR[0] is already filled.
for (int i = 1;i < (sizeof(OCRarray)/2);i++)
{ // populate OCR array
OCRarray[i] = (OCRo*(sqrt((i*speedUpdateSteps)+1)-sqrt((i*speedUpdateSteps))));
}
}
CL42T.h:
#ifndef CL42T_h
#define CL42T_h
#include "Arduino.h"
#include "StepperSpeedRamp.h"
class CL42T: public StepperSpeedRamp
{
public:
// register pointers
volatile uint8_t *directionPORT;
volatile uint8_t *directionDDR;
volatile uint8_t *directionPIN;
volatile uint8_t *enablePORT;
volatile uint8_t *enableDDR;
volatile uint8_t *enablePIN;
volatile uint8_t *alarmPORT;
volatile uint8_t *alarmDDR;
volatile uint8_t *alarmPIN;
volatile uint8_t *stepPORT;
volatile uint8_t *stepDDR;
volatile uint8_t *stepPIN;
volatile uint8_t *stepTCCRA;
volatile uint8_t *stepTCCRB;
volatile uint8_t *stepTCCRC;
volatile uint8_t *stepTIMSK;
volatile uint8_t *stepTIFR;
volatile uint8_t *stepTCNTH;
volatile uint8_t *stepTCNTL;
volatile uint8_t *stepOCRH;
volatile uint8_t *stepOCRL;
volatile uint8_t *stepOCRH2;
volatile uint8_t *stepOCRL2;
// timer register masks
uint8_t stepTCCRAmask;
uint8_t stepTCCRAOutput1Mask;
uint8_t stepTCCRAOutput2Mask;
uint8_t stepTCCRBmask;
uint8_t stepTCCRBstartMask;
uint8_t stepTimerOutputInterrupt1Mask;
// dual or single motor toggle variable
volatile int numMOTORS;
// step and control bits
volatile int stepBIT;
volatile int stepBIT2;
volatile int controlBIT;
volatile int controlBIT2;
// step target value
volatile int incrementSteps;
// single motor constructor:
CL42T(char StepPort, int StepBit, int ControlBit, int TimerNum, char TimerOutput, int PreScale);
// dual motor constructor:
CL42T(char StepPort1, int StepBit1, int ControlBit1, int TimerNum1, char TimerOutput1, int StepBit2, int ControlBit2, char TimerOutput2, int PreScale);
void SetupDrive(); // setup pointers, port directions, etc.
void ApplyNewSpeed(); // set OCRL/OCRH to OCRarray[0]
void SetDirection(char DriveDir); // set direction of CL42T stepper driver
void EnableDrive(); // enable CL42T stepper driver
void DisableDrive(); // disable CL42T stepper driver
void RunDrive(); // run single mmotor or both motors
void StopDrive(); // stop single motor or both motors
void Increment(int IncrementSteps); // run at maxSpeed for IncrementSteps
};
#endif
CL42T.cpp:
#include "Arduino.h"
#include "CL42T.h"
/*
* Single Motor Constructor:
* - set numMOTORS to 1
* - copy relevant parameters
* - set step pointers
* - set timer pointers and timer masks
*/
CL42T::CL42T(char StepPort, int StepBit, int ControlBit, int TimerNum, char TimerOutput, int PreScale)
:StepperSpeedRamp(PreScale)
{
// this is the single motor constructor
numMOTORS = 1;
// register pointers
directionPORT = &PORTC;
directionDDR = &DDRC;
directionPIN = &PINC;
enablePORT = &PORTA;
enableDDR = &DDRA;
enablePIN = &PINA;
alarmPORT = &PORTK;
alarmDDR = &DDRK;
alarmPIN = &PINK;
stepPORT = NULL;
stepDDR = NULL;
stepTCCRA = NULL;
stepTCCRB = NULL;
stepTCCRC = NULL;
stepTIMSK = NULL;
stepTIFR = NULL;
stepOCRH = NULL;
stepOCRL = NULL;
stepOCRH2 = NULL;
stepOCRL2 = NULL;
stepTCNTH = NULL;
stepTCNTL = NULL;
stepPIN = NULL;
// copy bit parameters
stepBIT = StepBit;
controlBIT = ControlBit;
// determine step pointers
switch (StepPort)
{
case 'B':
stepPORT = &PORTB;
stepDDR = &DDRB;
stepPIN = &PINB;
break;
case 'E':
stepPORT = &PORTE;
stepDDR = &DDRE;
stepPIN = &PINE;
break;
case 'H':
stepPORT = &PORTH;
stepDDR = &DDRH;
stepPIN = &PINH;
break;
case 'L':
stepPORT = &PORTL;
stepDDR = &DDRL;
stepPIN = &PINL;
break;
default:
break;
}
// determine timer pointers and set timer masks
switch (TimerNum)
{
case 1:
stepTCCRA = &TCCR1A;
stepTCCRB = &TCCR1B;
stepTCCRC = &TCCR1C;
stepTIMSK = &TIMSK1;
stepTIFR = &TIFR1;
stepTCNTL = &TCNT1L;
stepTCNTH = &TCNT1H;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM12); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput)
{
case 'A':
stepOCRL = &OCR1AL;
stepOCRH = &OCR1AH;
stepTCCRAOutput1Mask = (1<<COM1A0); // COMA - Toggle on compare match
stepTimerOutputInterrupt1Mask = (1<<OCIE1A); // COMA interrupt enable
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS11); // prescale 8
break;
default:
break;
}
break;
case 2:
stepTCCRA = &TCCR2A;
stepTCCRB = &TCCR2B;
stepTIMSK = &TIMSK2;
stepTIFR = &TIFR2;
stepTCNTL = &TCNT2;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM22); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput)
{
case 'A':
stepOCRL = &OCR2A;
stepTCCRAOutput1Mask = (1<<COM2A0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE2A);
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS21); // prescale 8
break;
default:
break;
}
break;
case 3:
stepTCCRA = &TCCR3A;
stepTCCRB = &TCCR3B;
stepTCCRC = &TCCR3C;
stepTIMSK = &TIMSK3;
stepTIFR = &TIFR3;
stepTCNTL = &TCNT3L;
stepTCNTH = &TCNT3H;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM32); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput)
{
case 'A':
stepOCRL = &OCR3AL;
stepOCRH = &OCR3AH;
stepTCCRAOutput1Mask = (1<<COM3A0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE3A);
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS31); // prescale 8
break;
default:
break;
}
break;
case 4:
stepTCCRA = &TCCR4A;
stepTCCRB = &TCCR4B;
stepTCCRC = &TCCR4C;
stepTIMSK = &TIMSK4;
stepTIFR = &TIFR4;
stepTCNTL = &TCNT4L;
stepTCNTH = &TCNT4H;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM42); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput)
{
case 'A':
stepOCRL = &OCR4AL;
stepOCRH = &OCR4AH;
stepTCCRAOutput1Mask = (1<<COM4A0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE4A);
break;
case 'B':
stepOCRL = &OCR4BL;
stepOCRH = &OCR4BH;
stepTCCRAOutput1Mask = (1<<COM4B0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE4B);
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS41); // prescale 8
break;
default:
break;
}
break;
case 5:
stepTCCRA = &TCCR5A;
stepTCCRB = &TCCR5B;
stepTCCRC = &TCCR5C;
stepTIMSK = &TIMSK5;
stepTIFR = &TIFR5;
stepTCNTL = &TCNT5L;
stepTCNTH = &TCNT5H;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM52); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput)
{
case 'A':
stepOCRL = &OCR5AL;
stepOCRH = &OCR5AH;
stepTCCRAOutput1Mask = (1<<COM5A0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE5A);
break;
case 'B':
stepOCRL = &OCR5BL;
stepOCRH = &OCR5BH;
stepTCCRAOutput1Mask = (1<<COM5B0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE5B);
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS51); // prescale 8
break;
default:
break;
}
break;
default:
break;
}
}
/*
* Dual Motor Constructor:
* - set numMOTORS to 2
* - copy relevant parameters
* - set step pointers
* - set timer pointers and timer masks
*/
CL42T::CL42T(char StepPort, int StepBit1, int ControlBit1, int TimerNum, char TimerOutput1, int StepBit2, int ControlBit2, char TimerOutput2, int PreScale)
:StepperSpeedRamp(PreScale)
{
// this is the dual motor constructor
numMOTORS = 2;
// register pointers
directionPORT = &PORTC;
directionDDR = &DDRC;
directionPIN = &PINC;
enablePORT = &PORTA;
enableDDR = &DDRA;
enablePIN = &PINA;
alarmPORT = &PORTK;
alarmDDR = &DDRK;
alarmPIN = &PINK;
stepPORT = NULL;
stepDDR = NULL;
stepTCCRA = NULL;
stepTCCRB = NULL;
stepTCCRC = NULL;
stepTIMSK = NULL;
stepTIFR = NULL;
stepOCRH = NULL;
stepOCRL = NULL;
stepOCRH2 = NULL;
stepOCRL2 = NULL;
stepTCNTH = NULL;
stepTCNTL = NULL;
// public pointer attributes
stepPIN = NULL;
// copy relevant parameters
stepBIT = StepBit1;
stepBIT2 = StepBit2;
controlBIT = ControlBit1;
controlBIT2 = ControlBit2;
// determine step pointers
switch (StepPort)
{
case 'H':
stepPORT = &PORTH;
stepDDR = &DDRH;
stepPIN = &PINH;
break;
case 'L':
stepPORT = &PORTL;
stepDDR = &DDRL;
stepPIN = &PINL;
break;
default:
break;
}
// determine timer pointers and set timer masks
switch (TimerNum)
{
case 4:
stepTCCRA = &TCCR4A;
stepTCCRB = &TCCR4B;
stepTCCRC = &TCCR4C;
stepTIMSK = &TIMSK4;
stepTIFR = &TIFR4;
stepTCNTL = &TCNT4L;
stepTCNTH = &TCNT4H;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM42); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput1)
{
case 'A':
stepOCRL = &OCR4AL;
stepOCRH = &OCR4AH;
stepTCCRAOutput1Mask = (1<<COM4A0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE4A); // COMA interrupt enable
break;
case 'B':
stepOCRL = &OCR4BL;
stepOCRH = &OCR4BH;
stepTCCRAOutput1Mask = (1<<COM4B0); // COMB - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE4B); // COMB interrupt enable
break;
default:
break;
}
switch (TimerOutput2)
{
case 'A':
stepOCRL2 = &OCR4AL;
stepOCRH2 = &OCR4AH;
stepTCCRAOutput2Mask = (1<<COM4A0); // COMA - Toggle
break;
case 'B':
stepOCRL2 = &OCR4BL;
stepOCRH2 = &OCR4BH;
stepTCCRAOutput2Mask = (1<<COM4B0); // COMB - Toggle
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS41); // prescale 8
break;
default:
break;
}
break;
case 5:
stepTCCRA = &TCCR5A;
stepTCCRB = &TCCR5B;
stepTCCRC = &TCCR5C;
stepTIMSK = &TIMSK5;
stepTIFR = &TIFR5;
stepTCNTL = &TCNT5L;
stepTCNTH = &TCNT5H;
stepTCCRAmask = 0; // COMA / COMB off, WGMn1:0 = 0 (CTC mode)
stepTCCRBmask = (1<<WGM52); // WGMn2 = 1 (CTC mode), decide prescale later
// set OCR pointers and timer output masks
switch (TimerOutput1)
{
case 'A':
stepOCRL = &OCR5AL;
stepOCRH = &OCR5AH;
stepTCCRAOutput1Mask = (1<<COM5A0); // COMA - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE5A); // COMA output interrupt enable
break;
case 'B':
stepOCRL = &OCR5BL;
stepOCRH = &OCR5BH;
stepTCCRAOutput1Mask = (1<<COM5B0); // COMB - Toggle
stepTimerOutputInterrupt1Mask = (1<<OCIE5B); // COMB output interrupt enable
break;
default:
break;
}
switch (TimerOutput2)
{
case 'A':
stepOCRL2 = &OCR5AL;
stepOCRH2 = &OCR5AH;
stepTCCRAOutput2Mask = (1<<COM5A0); // COMA - Toggle
break;
case 'B':
stepOCRL2 = &OCR5BL;
stepOCRH2 = &OCR5BH;
stepTCCRAOutput2Mask = (1<<COM5B0); // COMB - Toggle
break;
default:
break;
}
// adjust timer mask for prescale
switch (PreScale)
{
case 8:
stepTCCRBstartMask = (1<<CS51); // prescale 8
break;
default:
break;
}
break;
default:
break;
}
}
/*
* Setup:
* - Set control I/O registers
* - Clear timer registers
* - Setup timer, but don't start it
*/
void CL42T::SetupDrive()
{
// clear timer registers and any pending interrupt flags
*stepTCCRB = 0;
*stepTCCRA = 0;
*stepTCCRC = 0;
*stepTIMSK = 0;
*stepTIFR |= B00001111;
if (numMOTORS == 1)
{ // single motor drive
// set timer registers
*stepTCCRA = (stepTCCRAOutput1Mask);
*stepTCCRB = (stepTCCRBmask | stepTCCRBstartMask);
*stepTCNTH = 0;
*stepTCNTL = 0;
noInterrupts();
*stepOCRH = highByte(OCRarray[0]);
*stepOCRL = lowByte(OCRarray[0]);
interrupts();
// setup control I/O registers
*stepDDR |= (1<<stepBIT); // set step pin as OUTPUT
*stepPORT &= ~(1<<stepBIT); // pull step pin LOW
*directionDDR |= (1<<controlBIT); // set direction pin as OUTPUT
*directionPORT &= ~(1<<controlBIT); // set direction pin LOW
*enableDDR |= (1<<controlBIT); // set enable pin as OUTPUT
*enablePORT &= ~(1<<controlBIT); // set enable pin LOW
*alarmDDR &= ~(1<<controlBIT); // set alarm pin as INPUT
*alarmPORT |= (1<<controlBIT); // set alarm pin INPUT_PULLUP
}
else if (numMOTORS == 2)
{ // dual motor drive
// set timer registers
*stepTCCRA = (stepTCCRAOutput1Mask | stepTCCRAOutput2Mask);
*stepTCCRB = (stepTCCRBmask | stepTCCRBstartMask);
*stepTCNTH = 0;
*stepTCNTL = 0;
noInterrupts();
*stepOCRH = highByte(OCRarray[0]);
*stepOCRL = lowByte(OCRarray[0]);
*stepOCRH2 = highByte(OCRarray[0]);
*stepOCRL2 = lowByte(OCRarray[0]);
interrupts();
// setup control I/O registers
*stepDDR |= ((1<<stepBIT) | (1<<stepBIT2)); // set step pins as OUTPUT
*stepPORT &= ~((1<<stepBIT) | (1<<stepBIT2)); // pull step pin LOW
*directionDDR |= ((1<<controlBIT) | (1<<controlBIT2)); // set direction pins as OUTPUT
*directionPORT &= ~((1<<controlBIT) | (1<<controlBIT2)); // set direction pins LOW
*enableDDR |= ((1<<controlBIT) | (1<<controlBIT2)); // set enable pins as OUTPUT
*enablePORT &= ~((1<<controlBIT) | (1<<controlBIT2)); // set enable pins LOW
*alarmDDR &= ~((1<<controlBIT) | (1<<controlBIT2)); // set alarm pins as INPUT
*alarmPORT |= ((1<<controlBIT) | (1<<controlBIT2)); // set alarm pins INPUT_PULLUP
}
*stepTCCRB &= ~(stepTCCRBstartMask); // turn off timer
*stepTCNTH = 0; // set timer value to zero
*stepTCNTL = 0;
*stepTIFR |= B00001111; // clear any interrupt flags generated
}
void CL42T::ApplyNewSpeed()
{ // set OCR value from OCRarray[0]
if (numMOTORS == 1)
{
// set OCR
noInterrupts();
*stepOCRH = (highByte(OCRarray[0]));
*stepOCRL = (lowByte(OCRarray[0]));
interrupts();
}
else if (numMOTORS == 2)
{
// set OCR
noInterrupts();
*stepOCRH = (highByte(OCRarray[0]));
*stepOCRL = (lowByte(OCRarray[0]));
*stepOCRH2 = (highByte(OCRarray[0]));
*stepOCRL2 = (lowByte(OCRarray[0]));
interrupts();
}
}
void CL42T::SetDirection(char DriveDir)
{
if (numMOTORS == 1)
{
switch (DriveDir)
{
case 'F':
*directionPORT |= (1<<controlBIT); // direction pin HIGH
break;
case 'R':
*directionPORT &= ~(1<<controlBIT); // directon pin LOW
break;
default:
break;
}
}
else if (numMOTORS == 2)
{
switch (DriveDir)
{
case 'F':
*directionPORT |= ((1<<controlBIT) | (1<<controlBIT2)); // direction pins HIGH
break;
case 'R':
*directionPORT &= ~((1<<controlBIT) | (1<<controlBIT2)); // direction pins LOW
break;
default:
break;
}
}
}
void CL42T::EnableDrive()
{
if (numMOTORS == 1)
{
*enablePORT |= (1<<controlBIT); // enable pin HIGH
}
else if (numMOTORS == 2)
{
*enablePORT |= ((1<<controlBIT) | (1<<controlBIT2)); // enable pins HIGH
}
}
void CL42T::DisableDrive()
{
if (numMOTORS == 1)
{
*enablePORT &= ~(1<<controlBIT); // enable pin LOW
}
else if (numMOTORS == 2)
{
*enablePORT &= ~((1<<controlBIT) | (1<<controlBIT2)); // enable pins LOW
}
}
void CL42T::RunDrive()
{
if (incrementSteps == 0)
{
incrementSteps = -1; // if no distance given, indicate no motion tracking to ISR
}
accelDecelIndex = 0;
*stepTCNTH = 0; // clear counter value
*stepTCNTL = 0;
*stepTIFR |= B00001111; // clear any pending interrupt flags
*stepTCCRB |= stepTCCRBstartMask; // turn on timer
}
void CL42T::StopDrive()
{
*stepTCCRB &= ~(stepTCCRBstartMask); // turn off timer
*stepTIMSK &= ~(stepTimerOutputInterrupt1Mask); // disable output compare interrupt
incrementSteps = 0; // set incrementSteps back to zero
}
void CL42T::Increment(int IncrementSteps)
{
incrementSteps = IncrementSteps; // copy new step target
*stepTIMSK |= stepTimerOutputInterrupt1Mask; // enable timer output compare interrupt
RunDrive(); // start drive
}