Hello all. This post will be a long one, but I have ZERO programming experience, so I need a little guidance.
I am trying to generate step pulses and direction signals for 8 individual steppers asynchronously. I need to vary the number of steps before the direction signal is inverted on the fly, and I need to vary the time between step signals (rising edge triggers the step) on the fly.
This is what I came up with. Obviously, it is junk, and it doesn't work. The general idea was to use a timer interrupt at 1 micro intervals to set PORTC and increment a counter, and then do all the work to determine which individual bits should be on or off within the loop(). So when you run it, the interrupt hogs all the processing time (even though I had thought I made the interrupt pretty lean) and it takes forever for the loop() to do its thing.
//these arrays and functions are for rapidly changing the state of output pins. Only compatible with MEGA2560
byte volatile * const DDRConfig[] = {&DDRE,&DDRE,&DDRE,&DDRE,&DDRG,&DDRE,&DDRH,&DDRH,&DDRH,&DDRH,&DDRB,&DDRB,&DDRB,&DDRB,&DDRK,&DDRK,&DDRH,&DDRH,&DDRD,&DDRD,&DDRD,&DDRD,&DDRA,&DDRA,&DDRA,&DDRA,&DDRA,&DDRA,&DDRA,&DDRA,&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRD,&DDRG,&DDRG,&DDRG,&DDRL,&DDRL,&DDRL,&DDRL,&DDRL,&DDRL,&DDRL,&DDRL,&DDRB,&DDRB,&DDRB,&DDRB,&DDRF,&DDRF,&DDRF,&DDRF,&DDRF,&DDRF,&DDRF,&DDRF,&DDRK,&DDRK,&DDRK,&DDRK,&DDRK,&DDRK,&DDRK,&DDRK};
byte volatile * const PortConfig[] = {&PORTE,&PORTE,&PORTE,&PORTE,&PORTG,&PORTE,&PORTH,&PORTH,&PORTH,&PORTH,&PORTB,&PORTB,&PORTB,&PORTB,&PORTK,&PORTK,&PORTH,&PORTH,&PORTD,&PORTD,&PORTD,&PORTD,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTD,&PORTG,&PORTG,&PORTG,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTB,&PORTB,&PORTB,&PORTB,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK};
byte const PinConfig[] = {B00000001,B00000010,B00010000,B00100000,B00100000,B00001000,B00001000,B00010000,B00100000,B01000000,B00010000,B00100000,B01000000,B10000000,B01000000,B10000000,B00000010,B00000001,B00001000,B00000100,B00000010,B00000001,B00000001,B00000010,B00000100,B00001000,B00010000,B00100000,B01000000,B10000000,B10000000,B01000000,B00100000,B00010000,B00001000,B00000100,B00000010,B00000001,B10000000,B00000100,B00000010,B00000001,B10000000,B01000000,B00100000,B00010000,B00001000,B00000100,B00000010,B00000001,B00001000,B00000100,B00000010,B00000001,B00000001,B00000010,B00000100,B00001000,B00010000,B00100000,B01000000,B10000000,B00000001,B00000010,B00000100,B00001000,B00010000,B00100000,B01000000,B10000000};
void PinOn(int pin) {*PortConfig[pin] |= PinConfig[pin];}
void PinOff(int pin) {*PortConfig[pin] &= ~(PinConfig[pin]);}
//These variables are used for setup and maintainance during runtime.
byte outputRegister = B00000000; //this will contain a byte which represents what the state should be on all pins on PORTC
int pinB_assignment = 30; //this is just a counter used to assign pins to the 8 motors on program start
volatile unsigned long cnt = 0;
int usecs = 50;
unsigned long newcnt = 0;
unsigned long oldcnt = 0;
//This is the Motor class which will contain information specific to each individual motor.
class Motor
{
private:
volatile bool _pulsing = false;
bool _changeDir = false;
int _resolution = 6400;
int _stepRange = 100;
int _currentStep = 0;
int _stepInterval = 2;
int _dir = -1;
unsigned long _lastcnt = 0;
int _pinE, _pinA, _pinB, _pinHLFB;
public:
Motor(int _p1, int _p2, int _p3, float _res);
int getVar() {return PinConfig[_pinB];}; //just a method to check things during testing. was checking that all pins were assigned properly during setup
void Step();
void SetMotorAngle(float _range);
void SetMotorRPM(float _speed);
};
Motor::Motor(int _p1, int _p2, int _p3, float _res){
pinMode(_p1, OUTPUT);
pinMode(_p2, OUTPUT);
pinMode(_p3, INPUT_PULLUP);
PinOn(_p1);
PinOn(_p2);
_pinE = _p1;
_pinA = _p2;
_pinHLFB = _p3;
_resolution = _res;
_pinB = pinB_assignment;
pinB_assignment += 1;
}
//this method is to set the number of steps that will occur before the motor reverses direction, inverts _pinA
void Motor::SetMotorAngle(float _angle) {_stepRange = floor(_angle * (_resolution / 360));}
//this method is to set the rate at which the step pulses should occur. it is convoluted and untested. will clean up once a working method of generating step pulses is found
void Motor::SetMotorRPM(float _rpm) {_stepInterval = (1000000/((_resolution * (_rpm/60))))/((usecs*16)-1);}
//this method is called to do a step based on several conditions. if the step pulse is high, turn it off. otherwise, check to see if it is time to pulse again, if so, check to see if enough steps have been made to change direction, if so, change _dir, if not, do a step.
void Motor::Step() {
if (_pulsing) {
outputRegister &= ~(PinConfig[_pinB]);
_pulsing = false;
} else {
if ((cnt - _lastcnt) >= _stepInterval) {
_lastcnt = cnt;
if (_changeDir) {
if (_dir == 1) {
_dir = -1;
PinOff(_pinA);
} else {
_dir = 1;
PinOn(_pinA);
}
_changeDir = false;
} else {
outputRegister |= PinConfig[_pinB];
_pulsing = true;
_currentStep += _dir;
if ((_currentStep == _stepRange) | (_currentStep == 0)) {
_changeDir = true;
}
}
}
}
}
//declare some things.
Motor *MotorA = NULL, *MotorB = NULL, *MotorC = NULL, *MotorD = NULL, *MotorE = NULL, *MotorF = NULL, *MotorG = NULL, *MotorH = NULL;
//set up interrupt for compare match timer 5 compare register A, on interrupt, set PORTC to 8 bit global integer "outputRegister" and then increment cnt by 1.
ISR(TIMER5_COMPA_vect){
PORTC = outputRegister;
cnt++;
}
//setup here
void setup() {
// set up timer5 and compare registers, set up DDRC to output.
cli();
TCCR5A = 0x00;
TCCR5B = (_BV(WGM52) | _BV(CS50)); // no prescaler, CTC mode
OCR5A = ((usecs*16)-1); / /1MHz = 1us cycle, so "usecs" should = the number of micros before compare match occurs?
TIMSK5 = _BV(OCIE5A); //don't understand this, but it needs to be here. this changes if different compare register is used
DDRC = B11111111; //all pins on Port C should be set to output
sei();
//initialize some motors
MotorA = new Motor(22,23,69,6400); //create an instance of motor
MotorB = new Motor(24,25,68,6400); //and another
//MotorC = new Motor(26,27,67,6400); //the rest are disabled during testing
//MotorD = new Motor(28,29,66,6400);
//MotorE = new Motor(38,39,65,6400);
//MotorF = new Motor(40,41,64,6400);
//MotorG = new Motor(42,43,63,6400);
//MotorH = new Motor(44,45,62,6400);
Serial.begin(9600);//start serial communication
}
void loop() {
if (cnt > oldcnt) { //make sure we aren't doing unnecessary work
oldcnt = cnt;
MotorA->Step(); //call Step() as often as possible
MotorB->Step();
//MotorC->Step();
//MotorD->Step();
//MotorE->Step();
//MotorF->Step();
//MotorG->Step();
//MotorH->Step();
}
}
So here are my questions.
-
Am I saving any time by turning pins on or off via port manipulation using the array that I spent an hour writing out with some Excel help? (the index of DDRconfig[], PortConfig[], and PinConfig[] should match the pin number and return the information relevant to that physical pin, which can then be turned on or off with PinOn() or PinOff()). I thought I was being pretty sneaky here by loading all the info for every broken-out Mega pin into the arrays and then saving a bunch of time by using this info to set registers directly, but like I said, I don't know what I am doing and this may have been counterproductive.
-
Can I set up 8 PWM pins on the Mega, in two groups of 4 (separate timer for each group) and then vary the frequency on the fly, while also counting the number of pulses? Would this work? I have no idea how to count the number of pulses on PWM signals and I don't really understand the PWM stuff. I assume I would just use a 50% duty cycle (since I don't care about duty cycles) and modify the timer registers on the fly to adjust the frequencies.
-
How can I get an accurate count of micros()? Using a timer interrupt every microsecond to increment a variable seems like it eats all the processing power of the Arduino.
-
Does anyone have any better ideas on how to control these stepper motors?