copy lab_2 code to src folders
This commit is contained in:
349
src/lab_2/SimplisticRampStepper/SimplisticRampStepper.ino
Normal file
349
src/lab_2/SimplisticRampStepper/SimplisticRampStepper.ino
Normal file
@@ -0,0 +1,349 @@
|
||||
/* Stepper motor demonstration program written by Arthur Jones,
|
||||
4 November 2018. Implements a simplistic and ineffective ramping
|
||||
algorithm but provides framework for implementation of LeibRamp
|
||||
algorithm described by Aryeh Eiderman, http://hwml.com/LeibRamp.pdf
|
||||
|
||||
Makes use of background work and some aspects of code developed
|
||||
by Choaran Wang, 2017-18. This in turn incorporates some ideas
|
||||
used in the AccelStepper library:
|
||||
https://www.airspayce.com/mikem/arduino/AccelStepper/
|
||||
|
||||
Serial input aspects are based closely upon:
|
||||
http://forum.arduino.cc/index.php?topic=396450
|
||||
Example 4 - Receive a number as text and convert it to an int
|
||||
Modified to read a long */
|
||||
|
||||
// #define USEINTERRUPTS
|
||||
const int stepPin = 13;
|
||||
const int dirPin = 9;
|
||||
const bool FWDS = true;
|
||||
const bool BWDS = false;
|
||||
#ifdef USEINTERRUPTS
|
||||
const long ticksPerSec = 16000000; // Clock speed of Arduino
|
||||
#else
|
||||
const long ticksPerSec = 1000000; // microseconds in this case
|
||||
#endif
|
||||
|
||||
|
||||
/* Define permissible parameters for motor */
|
||||
// For testing by watching LED: try movements in order of 100 steps
|
||||
//float accelSteps=20; /* leave this as a variable as we may over-write it */
|
||||
//const float minSpeed = 2.0;
|
||||
//const float maxPermissSpeed = 20.0;
|
||||
//const float maxAccel = 10.0;
|
||||
//const long stepLengthMus = 10000;
|
||||
|
||||
// For lab testing with real motor: try movements in the order of 3000 steps
|
||||
float accelSteps=1000; /* leave this as a variable as we may over-write it */
|
||||
const float minSpeed=10.0;
|
||||
const float maxPermissSpeed=1000.0;
|
||||
const float maxAccel=500.0;
|
||||
const long stepLengthMus=100;
|
||||
|
||||
/* Intervals in milliseconds for user-defined timed loops */
|
||||
const long printInterval = 1000;
|
||||
|
||||
/* Global variables used for loop timing */
|
||||
unsigned long prevMillisPrint = 0; /* stores last time values were printed */
|
||||
|
||||
/* Global variables used in serial input */
|
||||
enum {numChars = 32};
|
||||
char receivedChars[numChars]; /* an array to store the received data */
|
||||
long dataNumber = 0; /* Value read from serial monitor input */
|
||||
boolean newData = false;
|
||||
|
||||
/* Global variables relating to stepper motor position counting etc. */
|
||||
long stepsToGo; /* Number of steps left to make in present movement */
|
||||
long targetPosition; /* Intended destination of motor for given movement */
|
||||
volatile long currentPosition = 0; /* Position in steps of motor relative to startup position */
|
||||
double maxSpeed; /* Maximum speed in present movement (not nec. max permitted) */
|
||||
bool direction; /* Direction of present movement: FWDS or BWDS */
|
||||
|
||||
volatile float p; /* Step interval in clock ticks or microseconds */
|
||||
float p1, ps; /* Minimum and maximum step periods */
|
||||
double deltaP; /* You'll be able to get rid of this later */
|
||||
double R; /* Multiplying constant used in Eiderman's algorithm */
|
||||
|
||||
/* Global variable used for noting previous time of a step in timed loop */
|
||||
long prevStepTime;
|
||||
|
||||
void setup()
|
||||
{
|
||||
long stepsToGo = 0;
|
||||
currentPosition = 0;
|
||||
goToPosition(dataNumber);
|
||||
pinMode(stepPin, OUTPUT);
|
||||
pinMode(dirPin, OUTPUT);
|
||||
Serial.begin(9600);
|
||||
Serial.println("Enter target position in number of steps and hit return");
|
||||
|
||||
// If USEINTERRUPTS is defined at the start of the program this section will be used
|
||||
#ifdef USEINTERRUPTS
|
||||
cli();
|
||||
TCCR1A = 0; // No output compare
|
||||
TCCR1B = 0;
|
||||
TCCR1B |= (1 << WGM12); //CTC mode
|
||||
OCR1A = 0; // Set to zero for the present time: catch this to switch interrupt off
|
||||
TCCR1B |= (1 << CS12); // 256 prescaler: overwritten in ISR
|
||||
TIMSK1 |= (1 << OCIE1A); //enable timer compare interrupt
|
||||
sei();
|
||||
#else //
|
||||
// Use built-in Arduino function micros() to get the time in microseconds since the program started running.
|
||||
// Documentation: https://www.arduino.cc/reference/en/language/functions/time/micros/
|
||||
prevStepTime = micros(); // This is a built-in Arduino function.
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
unsigned long currentMillis = millis();
|
||||
unsigned long currentMicros;
|
||||
recvWithEndMarker();
|
||||
stepsToGo = computeStepsToGo();
|
||||
if (convertNewNumber())
|
||||
{
|
||||
/* Only get to this stage if there was new data to convert */
|
||||
if (stepsToGo <= 0)
|
||||
{
|
||||
/* Only get to this stage if not busy, otherwise will have thrown away input */
|
||||
goToPosition(dataNumber);
|
||||
|
||||
/* Delete these and replace with Leib Ramp formulae */
|
||||
double maxInterval = ((double)ticksPerSec) / minSpeed;
|
||||
ps = ((double)ticksPerSec) / maxPermissSpeed;
|
||||
deltaP = (maxInterval - ps) / accelSteps;
|
||||
/* End of section requiring redefinitions */
|
||||
|
||||
stepsToGo = computeStepsToGo();
|
||||
maxSpeed = maxPermissSpeed;
|
||||
if (2 * accelSteps > stepsToGo)
|
||||
{
|
||||
/* Definiiton of S where ther is no constant speed period - check it is still applicable */
|
||||
accelSteps = (long)(stepsToGo / 2);
|
||||
/* Need to redefine maxSpeed here as we never fully accelerate */
|
||||
}
|
||||
|
||||
/* Will need to over-write these with correct initial value of p and p1, along with R */
|
||||
p = maxInterval;
|
||||
p1 = (double)ticksPerSec/minSpeed;
|
||||
/* End of section requiring redefinitions */
|
||||
|
||||
ps = ((double)ticksPerSec) / maxSpeed; /* Eq 7 in paper: this is OK */
|
||||
|
||||
#ifdef USEINTERRUPTS
|
||||
if (p != 0)
|
||||
{
|
||||
// Re-enable interrupts if non-zero steps
|
||||
TIMSK1 |= (1 << OCIE1A);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifndef USEINTERRUPTS
|
||||
|
||||
/* Timed loop for stepping, and associated coding */
|
||||
currentMicros = micros();
|
||||
if (currentMicros - prevStepTime >= p)
|
||||
{
|
||||
moveOneStep();
|
||||
prevStepTime = currentMicros;
|
||||
computeNewSpeed();
|
||||
}
|
||||
#endif
|
||||
/* Timed loop for printing */
|
||||
if (currentMillis - prevMillisPrint >= printInterval)
|
||||
{
|
||||
/* Save the last time output was printed */
|
||||
prevMillisPrint = currentMillis;
|
||||
printLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void moveOneStep()
|
||||
/* Move a single step, holding pulse high for delayMicroSeconds */
|
||||
{
|
||||
if (p != 0) /* p=0 is code for "don't make steps" */
|
||||
{
|
||||
digitalWrite(stepPin, HIGH);
|
||||
if (direction == FWDS)
|
||||
{
|
||||
/* Is something missing here? */
|
||||
currentPosition++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Is something missing here? */
|
||||
currentPosition--;
|
||||
}
|
||||
delayMicroseconds(stepLengthMus);
|
||||
digitalWrite(stepPin, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void computeNewSpeed()
|
||||
/* Calcuate new value of step interval p based on constants defined in loop() */
|
||||
{
|
||||
/* You may need to declare some temporary variables for this function... */
|
||||
stepsToGo = computeStepsToGo();
|
||||
|
||||
if (stepsToGo == 0)
|
||||
{
|
||||
p = 0; // Not actually a zero step interval, used to switch stepping off
|
||||
return;
|
||||
}
|
||||
else if (stepsToGo > accelSteps && (long)p > long(ps))
|
||||
/* Speeding up */
|
||||
{
|
||||
/* Delete this simplistic change to p and replace with something else */
|
||||
p -= deltaP;
|
||||
}
|
||||
else if (stepsToGo <= accelSteps)
|
||||
/* Slowing down */
|
||||
{
|
||||
/* Delete this simplistic change to p and replace with something else */
|
||||
p += deltaP;
|
||||
}
|
||||
else
|
||||
/* Running at constant speed */
|
||||
{
|
||||
/* For simpplistic approach, p=p so do nothing to it.
|
||||
But you will need to put something here for Leib ramp ... */
|
||||
}
|
||||
/* Update to step interval based on Leib ramp algorithm, using temporary variables */
|
||||
|
||||
/* Need to ensure rounding error does not cause drift outside acceptable interval range:
|
||||
replace p with relevant bound if it strays outside - so need to write some code here */
|
||||
|
||||
|
||||
}
|
||||
|
||||
long computeStepsToGo()
|
||||
/* Work out how far the stepper motor still needs to move */
|
||||
{
|
||||
if (direction == FWDS)
|
||||
{
|
||||
return targetPosition - currentPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
return currentPosition - targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
void goToPosition(long newPosition)
|
||||
/* Set the target position and determine direction of intended movement */
|
||||
{
|
||||
targetPosition = newPosition;
|
||||
if (targetPosition - currentPosition > 0)
|
||||
{
|
||||
direction = FWDS;
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = BWDS;
|
||||
}
|
||||
}
|
||||
|
||||
void recvWithEndMarker()
|
||||
/* Receive data from serial port finishing with "newline" character.
|
||||
Based on http://forum.arduino.cc/index.php?topic=396450 Example 4 */
|
||||
{
|
||||
static byte ndx = 0;
|
||||
char endMarker = '\n';
|
||||
char rc;
|
||||
|
||||
if (Serial.available() > 0)
|
||||
{
|
||||
rc = Serial.read();
|
||||
|
||||
if (rc != endMarker)
|
||||
{
|
||||
receivedChars[ndx] = rc;
|
||||
ndx++;
|
||||
if (ndx >= numChars)
|
||||
{
|
||||
ndx = numChars - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedChars[ndx] = '\0'; // terminate the string
|
||||
ndx = 0;
|
||||
newData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool convertNewNumber()
|
||||
/* Converts character string to long integer only if there are new
|
||||
data to convert, otherwise returns false */
|
||||
{
|
||||
if (newData)
|
||||
{
|
||||
dataNumber = 0.0; // new for this version
|
||||
dataNumber = atol(receivedChars); // new for this version
|
||||
newData = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void printLoop()
|
||||
/* Print current position of stepper using timed loop */
|
||||
{
|
||||
/* Sample all counters one after the other to avoid delay-related offsets */
|
||||
Serial.print("Current position = ");
|
||||
Serial.print(currentPosition);
|
||||
Serial.print("\r\n");
|
||||
}
|
||||
|
||||
#ifdef USEINTERRUPTS
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
/* Interrupt service routine which essentially just calls moveOneStep and computeNewSpeed.
|
||||
However, it also changes the prescale value on-the-fly so that the full range of possible
|
||||
step rates can be exploited, from around 0.25 Hz upwards, limited by step pulse width. */
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
// Disable interrupt to avoid endless calling of interrupt if not needed
|
||||
TIMSK1 &= !(1 << OCIE1A);
|
||||
}
|
||||
moveOneStep();
|
||||
|
||||
/* Adapt prescaler to keep OCR1A as large as possible within acceptable range */
|
||||
if (p < 65536)
|
||||
{
|
||||
// Prescaler 1
|
||||
OCR1A = (long)p - 1;
|
||||
TCCR1B = (TCCR1B & 0xF8) | 0x01;
|
||||
}
|
||||
else if (p < 524288)
|
||||
{
|
||||
// Prescaler 8
|
||||
OCR1A = ((long)p >> 3) - 1;
|
||||
TCCR1B = (TCCR1B & 0xF8) | 0x02;
|
||||
}
|
||||
else if (p < 4194304)
|
||||
{
|
||||
// Prescaler 64
|
||||
OCR1A = ((long)p >> 6) - 1;
|
||||
TCCR1B = (TCCR1B & 0xF8) | 0x03;
|
||||
}
|
||||
else if (p < 16777216)
|
||||
{
|
||||
// Prescaler 256
|
||||
OCR1A = ((long)p >> 8) - 1;
|
||||
TCCR1B = (TCCR1B & 0xF8) | 0x04;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prescaler 1024
|
||||
OCR1A = ((long)p >> 10) - 1;
|
||||
TCCR1B = (TCCR1B & 0xF8) | 0x05;
|
||||
}
|
||||
computeNewSpeed();
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user