Using Teensy 3.6 to Drive Multiple Integrated Steppers

Status
Not open for further replies.

jwllorens

New member
Hello all,

Forwarning, I am very new to both programming and electronics in general (just a few weeks of experience) but I do have some basic knowledge of how digital circuits work in general.

I am working on a project and would like to determine the feasibility of using a Teensy 3.6 to drive up to 6 independent stepper motors asynchronously. These steppers are integrated and have a driver built in, so all they require is step and direction signals. They can even be programmed to automatically do all the acceleration calculations so I can just pump the step signals in at the maximum velocity I want the motor to ramp up to without having to do those calculations on the microcontroller. However, they require 5v logic on the step and direction signals. I have successfully gotten two of them to spin using an Arduino Mega (ATMega2560) but ran into issues when building on the sketch and would like to try a faster microprocessor with more hardware timer and interrupt capability to give me more control over the motors in real time.

Here is the datasheet for the steppers (Note that the motors being used are the Clearpath SDHP motors): https://www.teknic.com/files/downloads/clearpath_user_manual.pdf

My questions are as follows:

1) Can the six Teensy 3.6 PWM timers be used to generate an interrupt at custom intervals, or am I restricted to using the interval timers? I wasn't able to discern this from the datasheet. I want to do something similar to what is shown in the AVR code at the end of the post.

2) If the Teensy 3.6 is a good option, what logic level shifter would you recommend for stepping up to 5v? It looks like https://www.sparkfun.com/products/12009 would work fine as long as the rising edge of my step pulses are at least roughly 50ns apart, which will obviously be the case since I can't realistically be doing interrupts that fast anyways.


Here is the code I was using on the Arduino Mega to test two motors at once (I want to run 6). I would obviously have to clean it up to use digitalWriteFast and change the interrupts and timer registers for the Teensy.

Code:
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]);
}

class Motor
{
  private:
    volatile bool _moving = false;
    volatile bool _pulsing = false;
    volatile unsigned long _tickCnt = 0;
    volatile unsigned long _moveTicks = 0;
    volatile unsigned long _offset = 0;
    volatile int _MotorID;
    volatile int _moveDir = 1;
    unsigned long _resolution = 6400;
    unsigned long _moveSpd = 0;
    unsigned long _moveAng = 0;
    int _pinE, _pinA, _pinB, _pinHLFB;
  public:
    Motor(int _p1, int _p2, int _p3, int p4, int _res, int _ID);
    bool IsMoving();
    void Oscillate();
    void Move();
    void SetParams(unsigned long _ang, unsigned long _spd, int _dir);
    void Tick();
};

void Motor::SetParams(unsigned long _ang, unsigned long _spd, int _dir) {
  _moveTicks = ((_ang * _resolution) / 360);
  _moveSpd = 60000000 / (_spd * _resolution);
  _moveDir = _dir;
}

Motor::Motor(int _p1, int _p2, int _p3, int _p4, int _res, int _ID) {
  _MotorID = _ID;
  pinMode(_p1, OUTPUT);
  pinMode(_p2, OUTPUT);
  pinMode(_p3, OUTPUT);
  pinMode(_p4, INPUT_PULLUP);
  PinOn(_p1);
  PinOn(_p2);
  _pinE = _p1;
  _pinA = _p2;
  _pinHLFB = _p4;
  _pinB = _p3;
  _resolution = _res;
  
}

bool Motor::IsMoving() {
  return _moving;
}

void Motor::Oscillate() {
  if (!_moving) {
    if (_moveDir > 0) {
      Move();
      _moveDir = -1;
    } else {
      Move();
      _moveDir = 1;
    }
  }
}

void Motor::Tick() {
  if (_pulsing) {
    PinOff(_pinB);
    _pulsing = false;
    if (_tickCnt >= _moveTicks) {
      switch (_MotorID) {
        case 0:
          TIMSK5 &= ~(_BV(OCIE5A));  //turns off compare interrupts when the move is done
          break;
        case 1:
          TIMSK4 &= ~(_BV(OCIE4A));
          break;
        case 2:
          TIMSK3 &= ~(_BV(OCIE3A));
          break;
      }
      _tickCnt = 0;
      _moving = false;
    } else {
      _tickCnt++;
      _offset += _moveDir; 
    }
  } else {
    PinOn(_pinB);
    _pulsing = true;
  }
}

void Motor::Move() {
  _moving = true;
  if (_moveDir == 1) {
    PinOn(_pinA);
  } else {
    PinOff(_pinA);
  }
  switch (_MotorID) {
    case 0:
      cli();
      OCR5A  = _moveSpd;
      TCNT5 = 0;
      TIMSK5 |= _BV(OCIE5A);
      sei();
      break;
    case 1:
      cli();
      OCR4A  = _moveSpd;
      TIMSK4 |= _BV(OCIE4A);
      TCNT4 = 0;
      sei();
      break;
    case 2:
      cli();
      OCR3A  = _moveSpd;
      TIMSK3 |= _BV(OCIE3A);
      TCNT3 = 0;
      sei();
      break;
  }
}

Motor *MotorA = NULL, *MotorB = NULL, *MotorC = NULL;
int Acommand = 0, Bcommand = 0, Ccommand = 0;

void setup() {
  cli();
  TCCR5A = TCCR5B = 0x00;
  TCCR5B = (_BV(CS51) | _BV(WGM52));
  TCCR4A = TCCR4B = 0x00;
  TCCR4B = (_BV(CS41) | _BV(WGM42));
  TCCR3A = TCCR3B = 0x00;
  TCCR3B = (_BV(CS31) | _BV(WGM32));
  sei();
  MotorA = new Motor(41, 42, 43, 40, 800, 0);
  MotorB = new Motor(37, 38, 39, 36, 800, 1);
  MotorC = new Motor(33, 34, 35, 32, 800, 2);
  Serial.begin(9600);
}

ISR(TIMER5_COMPA_vect) {
  MotorA->Tick();
}

ISR(TIMER4_COMPA_vect) {
  MotorB->Tick();
}

ISR(TIMER3_COMPA_vect) {
  MotorC->Tick();
}

void loop() {
      if (!MotorA->IsMoving()) {
        switch (Acommand) {
          case 0:
            MotorA->SetParams(360, 150, -1);
            break;
          case 1:
            MotorA->SetParams(360, 175, 1);
            break;
          case 2:
            MotorA->SetParams(360, 200, -1);
            break;
          case 3:
            MotorA->SetParams(360, 225, 1);
            break;
        }
        MotorA->Move();
        if (Acommand == 17) {
          Acommand = 0;
        } else {
          Acommand++;
        }
      }
    if (!MotorB->IsMoving()) {
      switch (Bcommand) {
        case 0:
          MotorB->SetParams(90, 200, 1);
          break;
        case 1:
          MotorB->SetParams(120, 175, 1);
          break;
        case 2:
          MotorB->SetParams(150, 150, -1);
          break;
        case 3:
          MotorB->SetParams(180, 125, 1);
          break;
      }
      MotorB->Move();
      if (Bcommand == 8) {
        Bcommand = 0;
      } else {
        Bcommand++;
      }
    }
}
 
Status
Not open for further replies.
Back
Top