Feb 22, 2012

Easy Driver With Arduino

 
Part I              (Maximum Stepper Speed)
Go to Part II   (Adjust max Stepper Current)
Go to Part III (Sleep an Enable)
 
 
 
Click Here to see and download the design of a PCB implementing this circuit.
 







An important thing to knwo about your stepper, is the maximum speed at witch it can be driven.
To know this, you can follow this LINK;

you need to knwo the specs of your stepper.
For my stepper:

  Voltage:                                  12v
  Inductance:                             46+-20% mH           then i take 50
  Steps per revolution:              Step Angle = 1.8º     360 / 1.8 = 200 Steps/rev
  Current:                                  0.33 A                       i take 135 mA because with easy
                                                                                   driver you can adjust the maximun current
                                                                                   with the potenciometer
                                                                                   (i will explain this in a future post)

then following the calculs from the previous link:

  T = 50 * 0.135 * 2 / 12 = 1.125 ms for a single Step

The default configuration of EasyDriver is eighth stepping then:

  T = 1.125 / 8 = 0.140 ms = 140 us for a single Step

given this we take a prescaler of 128 for the Timer2 (see coments in the code)

  128 / 16000000 = 0.000008 s-1 = 8 us-1         ( the frequency of the processor in the
                                                                              Arduino Uno is 16 MHz)

  Timer2 actulizes its counter every 8 us then, 140 / 8 = 17.5

then we have to program the counter for the Timer2 to 18 ( 18 = ceil(17.5) ), because of that we have a success of Timer2 every 18 * 8us = 144 us

and the speed for our stepper will be: 144 us * 1600 Steps/Revolution = 0.2304 s/Revolution

that is 1 / 0.2304 = 4.34 Revolutions / s , wich is an aproximation to the real speed, because there is an extra delay between steps introduced by the code itself  ( maybe 20 us extra delay ).


To see this working copy the following code to Arduino IDE, set the proper value for  this variables
      microSteping
      stepsPerRev
      l
      v
      iMax
 and run it.

This program also implements acceleration-desacceleration. You can change the amount of acceleration, changing the value of  'accelDuring' variable ( for example it accelerates during 10 steps the run at constant speed and finaly it desaccelerates during 10 steps ).
If you don't want acceleration set it to 0 ( accepted values are 0 - 255 ).



//http://www.daycounter.com/Calculators/Stepper-Motor-Calculator.phtml
//Rev/sec = V/(L*2*Imax)/(steps/rev)  (Imax en A, L en mili Herns) p.e. 12/(46*2*0.33)/200
//T = L*Imax*2/V (T miliSegons/step)
  
  #define DIR_PIN 7
  #define STEP_PIN 6
  #define sbi(sfr,bit)  ( sfr |=   _BV(bit)  )         // a l'antiga manera. cbi() i sbi() son funcions obsoletes
  #define cbi(sfr,bit)  ( sfr &= ~(_BV(bit)) )  
  //  MS1  MS2  Resolution
  //  L    L    00 Full step (2 phase)
  //  H    L    10 Half step
  //  L    H    01 Quarter step
  //  H    H    11 Eighth step
  //
  // per defecte a la easy driver MS1 = MS2 = HIGH
  
  float microStepping = 8;                             // Valor establert a la easyDriver ( MS1, MS2 )
  long stepsPerRev = 200;                              // característica del motor
  float l = 50;                                        // = 46 +- 20% (el pitjor dels casos es 55.2) característica del motor
  float v = 12;                                        // característica del motor
  float iMax = 0.135;                                  // mesurat amb el tester (cal dividir el resultat per 2 (hi ha 2 bobines))
                                                       // amb la EasyDriver podem controlar iMax abm el potenciòmetre
                                                       
  float ocr2a;                                         // valor de OCR2A ( que depèn de la velocitat màxima del Stepper )
  int ocr2aIni;                                        // valor inicial de OCR2A donada l'acceleració
  
  int accelDuring = 10;                                // Accelera i desaccelera durant 10 Steps
                                                       
  int downCounter = accelDuring;                       // el nombre d'Steps que falten fins acabar la desacceleració
  int upCounter = accelDuring;                         // el nombre d'Steps que falten fins acabar l'acceleració
  int accelDelta;                                      // valor en que varia OCR2A durant l'acceleració 
                                                       // ( p.e. si ha de pasar de 255 a 19 en 10 steps decremeta de 23 en 23 i começa a 249 )
                                                       
  long stepsLeft = 0;                                  // nombre d'Steps que manque per acabar
  boolean running = false;                             // motor is running?
  
  
  
  void setup() {
    unsigned int usDelay;                              // temps d'espera entre Steps en us
      
    Serial.begin(115200);
    pinMode(DIR_PIN, OUTPUT);
    pinMode(STEP_PIN, OUTPUT);
  
    usDelay = l * 2000 * iMax / v / microStepping;     // temps d'espera entre steps en microsegons  ( T= L*Imax*2/V (T miliSegons/step) )
    ocr2a = ceil((float)usDelay / 8.0);                // l'enter mes proper donat l'interval del Timer2 ( 8 us )
  
    Serial.println(ocr2a);
  
    if(accelDuring > 0){
      accelDelta = (255 - int(ocr2a) ) / accelDuring;  // OCR2A pasarà del enter mes proper a 255 fins a ocr2a
                                                       // el valor que hem calculat per la velocitat màxima
    Serial.println(accelDelta);
      
      if(accelDelta<=0){
        if(ocr2a < 255){                               // la durada de l'acceleració no pot ser tan gran com esperem
          accelDelta =1;
          ocr2aIni = 255;
          accelDuring = 255 - ocr2a;                   // durada màxima de l'acceleració permesa
          
        }else{                                         // ocr2a = 255, no hi pot haver acceleració
          accelDelta = 0;
          accelDuring = 0;
          ocr2aIni = ocr2a;
        }
        
      }else                                            // acceleració segons la definició
        ocr2aIni = ocr2a + accelDelta * accelDuring;   // calcula el OCR2A inicial donada l'acceleració
        
    }else                                              // no hi ha acceleració
      ocr2aIni = ocr2a;
  
    Serial.println(ocr2aIni);
  
    setTimer2(ocr2aIni);
  
    Serial.print("Inter Step Delay: ");
    Serial.print(usDelay);
    Serial.println(" us");
    Serial.print("Max Speed: ");
    Serial.print( 60000000.0 / ((float)stepsPerRev * microStepping * (usDelay)) );
    Serial.println(" RPM");
    Serial.print("Inter Step Delay programat a Timer2: ");
    Serial.println((int( ceil( (float)usDelay / 8.0 ) ))*8);
    Serial.print("Max Speed Real: ");
    Serial.print( 60000000.0 / ((float)stepsPerRev * microStepping * ((int( ceil( (float)usDelay / 8.0 ) ))*8)) );
  }//// 
  
  
  
  long nVoltes = 1;
  long nSteps = nVoltes * stepsPerRev * microStepping;
  

  void loop(){
    if(!running){
 delay(1000);
      driveStepper(nSteps);
      nSteps = -nSteps;
    }
  }////
  
  
  
  
  
  
  void driveStepper(long lnSteps){
    int dir = (lnSteps > 0)? HIGH:LOW;
    digitalWrite(DIR_PIN,dir);
  
    lnSteps = abs(lnSteps);
  
    OCR2A = ocr2aIni;                // reinicialitza el contador (permet acceleració)
    upCounter = accelDuring;
    downCounter = accelDuring;
    running = true;
    stepsLeft = lnSteps;
  }////
  
  
  
  void setTimer2(int lusDelay) {
  
    //http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
    // The ATmega328P has three timers known as Timer 0, Timer 1, and Timer 2
    // Each timer has two output compare registers 
    // Each of the timers has a prescaler that generates the timer clock by dividing
    //         the system clock by a prescale factor such as 1, 8, 64, 256, or 1024
    //         Note that Timer 2 has a different set of prescale values from the other timers
    // The Arduino has a system clock of 16MHz and the timer clock frequency will be
    //         the system clock frequency divided by the prescale factor
    // The timers can also generate interrupts on overflow and/or match against either output compare register
    //
    // TCCRnA and TCCRnB. The Timer/Counter Control Registers hold the main control bits
    //            for the timer. (Note that TCCRnA and TCCRnB do not correspond to the outputs A and B.) 
    // TCCRnA and TCCRnB hold several groups of bits: 
    //     Waveform Generation Mode bits (WGM): these control the overall mode of the timer.
    //         (These bits are split between TCCRnA and TCCRnB.) 
    //     Clock Select bits (CS): these control the clock prescaler 
    //     Compare Match Output A Mode bits (COMnA): these enable/disable/invert output A 
    //     Compare Match Output B Mode bits (COMnB): these enable/disable/invert output B 
    //
    // OCRnA and OCRnB. The Output Compare Registers set the levels at which outputs A and B will be affected
    //
    // TIMSK2 – Timer2 Interrupt Mask Register
    //     OCIE2B: Timer2 Output Compare Match B Interrupt Enable
    //     OCIE2A: Timer2 Output Compare Match A Interrupt Enable
    //     TOIE2:  Timer2 Overflow Interrupt Enable
    //
    // Timer 1 is a 16-bit timer and has additional modes. Timer 2 has different prescaler values
    //
    // The Arduino uses Timer 0 internally for the millis() and delay() functions
    // OC0A pin  6   OC0B pin  5
    // OC1A pin  9   OC1B pin 10
    // OC2A pin 11   OC2B pin  3 
  
    cli();                           // plana 10. Bit 7 Clear interrupts
  
    cbi(TCCR2A, COM2A0 );            // plana 158. Table 17-2. Normal port operation, OC0A disconnected. 
    cbi(TCCR2A, COM2A1 );            // 00 deconecta el pin A del timer2
  
    cbi(TCCR2A, WGM20  );            // TCCR2A Regitre A de control del timer 2
    sbi(TCCR2A, WGM21  );       // plana 160 i 149. Clear Timer on Compare
    cbi(TCCR2B, WGM22  );            // WGM2 = 010 ==> mode of operation Clear Timer on Compare (CTC)
                                     // The counter value TCNT2 increases until TCNT2 == OCR2A, 
                                     // and then counter (TCNT2) is cleared
                                     // repeteix el cilce 0 --> valor ( valor clocks )
  
    sbi(TCCR2B, CS22   );            // TCCR2B Regitre B de control del timer 2
    cbi(TCCR2B, CS21   );       // Plana 162
    sbi(TCCR2B, CS20   );            // CS2 = 101 prescaler = 128 ==> 128/16.000.000 = 0.000008 s-1 = 8us-1
                                     // màxim temps que es pot contar = 8 * 255 = 2040 us
  
    sbi(TIMSK2, OCIE2A  );           // Timer2 Output Compare Match A Interrupt Enable. plana 163
  
    cbi(ASSR, AS2);              // AS2 = 0 clocked from the I/O clock
                                     // plana 164. Asynchronous Status Register
                                     // When AS2 is written to zero, Timer/Counter2 is clocked from the I/O clock, clkI/O.
                                     // When AS2 is written to one, Timer/Counter2 is clocked from a crystal Oscillator
                                     // connected to the Timer Oscillator 1 (TOSC1) pin.    
  
    OCR2A = lusDelay;                //  si p.e. OCR2A = 21 --> 21*0.000008 = 168us-1  . cridem la funcio un cop cada 168 us. plana 162
  
    TCNT2 = 0;                       // reset Timer2 counter
  
    sei();                           // Enable Interrupts
  }////
  
  
  
  void stopTimer2() {
    cbi(TIMSK2, OCIE2A  );           // Timer2 Output Compare Match A Interrupt Disable. plana 163
  }////
  
  
  
                                     
  ISR(TIMER2_COMPA_vect) {           // Timer2 Output Compare Match A Interrupt
                                     // definició dels vectors a Arduino\hardware\tools\avr\avr\include\iom328p.h     
                                     // definitions for ATmega328P
    if(stepsLeft > 0){
      if(stepsLeft == downCounter){  // ha de desaccelerar
        downCounter--;
        OCR2A += accelDelta;         // incrementa el temps entre Steps en CS22:0 us (el proper Step es produirà 8 us mes tard)
      }
      else if(upCounter >0){         // ha d'accelerar
        upCounter--;
        OCR2A -= accelDelta;         // decrementa el temps entre Steps en CS22:0 us (el proper Step es produirà 8 us abans)
      }
      digitalWrite(STEP_PIN, HIGH);  // envia el pols a la EasyDriver per avançar 1 Step
      digitalWrite(STEP_PIN, LOW);   // A low-to-high transition on the STEP input sequences the translator 
                                     // and advances the motor one increment.       
                                     // Minimum STEP Pulse Width 1.0 μs (plana 7 de 'A3967.pdf')
                                     // la intruccio NOP triga 1 Clock ( 0,0625 us = 62.5 ns ) per tant
                                     // la instrucció digitalWrite ha de trigar més de 16 Clocks
                                     // mirar el codi a 'wiring_digital.c'
                                     // segons els forums 'digitalWrite()' sembla trigar entre 6 i 9 us
                                     // depenent de si el port es PWM o no
                                     // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230286016
      stepsLeft--;
    }
    else                             // para el motor
      running = false;
  }////