Trinamic TMC26X Stepper Driver for Arduino
TMC26XStepper.cpp
Go to the documentation of this file.
00001 /*
00002  TMC26XStepper.cpp - - TMC26X Stepper library for Wiring/Arduino - Version 0.1
00003  
00004  based on the stepper library by Tom Igoe, et. al.
00005  
00006  Copyright (c) 2011, Interactive Matter, Marcus Nowotny
00007  
00008  Permission is hereby granted, free of charge, to any person obtaining a copy
00009  of this software and associated documentation files (the "Software"), to deal
00010  in the Software without restriction, including without limitation the rights
00011  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00012  copies of the Software, and to permit persons to whom the Software is
00013  furnished to do so, subject to the following conditions:
00014  
00015  The above copyright notice and this permission notice shall be included in
00016  all copies or substantial portions of the Software.
00017  
00018  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00021  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00022  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00023  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00024  THE SOFTWARE.
00025  
00026  */
00027 
00028 #if defined(ARDUINO) && ARDUINO >= 100
00029         #include <Arduino.h>
00030 #else
00031         #include <WProgram.h>
00032 #endif
00033 #include <SPI.h>
00034 #include "TMC26XStepper.h"
00035 
00036 //some default values used in initialization
00037 #define DEFAULT_MICROSTEPPING_VALUE 32
00038 
00039 //TMC26X register definitions
00040 #define DRIVER_CONTROL_REGISTER 0x0ul
00041 #define CHOPPER_CONFIG_REGISTER 0x80000ul
00042 #define COOL_STEP_REGISTER  0xA0000ul
00043 #define STALL_GUARD2_LOAD_MEASURE_REGISTER 0xC0000ul
00044 #define DRIVER_CONFIG_REGISTER 0xE0000ul
00045 
00046 #define REGISTER_BIT_PATTERN 0xFFFFFul
00047 
00048 //definitions for the driver control register
00049 #define MICROSTEPPING_PATTERN 0xFul
00050 #define STEP_INTERPOLATION 0x200ul
00051 #define DOUBLE_EDGE_STEP 0x100ul
00052 #define VSENSE 0x40ul
00053 #define READ_MICROSTEP_POSTION 0x0ul
00054 #define READ_STALL_GUARD_READING 0x10ul
00055 #define READ_STALL_GUARD_AND_COOL_STEP 0x20ul
00056 #define READ_SELECTION_PATTERN 0x30ul
00057 
00058 //definitions for the chopper config register
00059 #define CHOPPER_MODE_STANDARD 0x0ul
00060 #define CHOPPER_MODE_T_OFF_FAST_DECAY 0x4000ul
00061 #define T_OFF_PATTERN 0xful
00062 #define RANDOM_TOFF_TIME 0x2000ul
00063 #define BLANK_TIMING_PATTERN 0x18000ul
00064 #define BLANK_TIMING_SHIFT 15
00065 #define HYSTERESIS_DECREMENT_PATTERN 0x1800ul
00066 #define HYSTERESIS_DECREMENT_SHIFT 11
00067 #define HYSTERESIS_LOW_VALUE_PATTERN 0x780ul
00068 #define HYSTERESIS_LOW_SHIFT 7
00069 #define HYSTERESIS_START_VALUE_PATTERN 0x78ul
00070 #define HYSTERESIS_START_VALUE_SHIFT 4
00071 #define T_OFF_TIMING_PATERN 0xFul
00072 
00073 //definitions for cool step register
00074 #define MINIMUM_CURRENT_FOURTH 0x8000ul
00075 #define CURRENT_DOWN_STEP_SPEED_PATTERN 0x6000ul
00076 #define SE_MAX_PATTERN 0xF00ul
00077 #define SE_CURRENT_STEP_WIDTH_PATTERN 0x60ul
00078 #define SE_MIN_PATTERN 0xful
00079 
00080 //definitions for stall guard2 current register
00081 #define STALL_GUARD_FILTER_ENABLED 0x10000ul
00082 #define STALL_GUARD_TRESHHOLD_VALUE_PATTERN 0x17F00ul
00083 #define CURRENT_SCALING_PATTERN 0x1Ful
00084 #define STALL_GUARD_CONFIG_PATTERN 0x17F00ul
00085 #define STALL_GUARD_VALUE_PATTERN 0x7F00ul
00086 
00087 //definitions for the input from the TCM260
00088 #define STATUS_STALL_GUARD_STATUS 0x1ul
00089 #define STATUS_OVER_TEMPERATURE_SHUTDOWN 0x2ul
00090 #define STATUS_OVER_TEMPERATURE_WARNING 0x4ul
00091 #define STATUS_SHORT_TO_GROUND_A 0x8ul
00092 #define STATUS_SHORT_TO_GROUND_B 0x10ul
00093 #define STATUS_OPEN_LOAD_A 0x20ul
00094 #define STATUS_OPEN_LOAD_B 0x40ul
00095 #define STATUS_STAND_STILL 0x80ul
00096 #define READOUT_VALUE_PATTERN 0xFFC00ul
00097 
00098 //default values
00099 #define INITIAL_MICROSTEPPING 0x3ul //32th microstepping
00100 
00101 //debuging output
00102 //#define DEBUG
00103 
00104 /*
00105  * Constructor
00106  * number_of_steps - the steps per rotation
00107  * cs_pin - the SPI client select pin
00108  * dir_pin - the pin where the direction pin is connected
00109  * step_pin - the pin where the step pin is connected
00110  */
00111 TMC26XStepper::TMC26XStepper(int number_of_steps, int cs_pin, int dir_pin, int step_pin, unsigned int current, unsigned int resistor)
00112 {
00113         //we are not started yet
00114         started=false;
00115     //by default cool step is not enabled
00116     cool_step_enabled=false;
00117         
00118         //save the pins for later use
00119         this->cs_pin=cs_pin;
00120         this->dir_pin=dir_pin;
00121         this->step_pin = step_pin;
00122     
00123     //store the current sense resistor value for later use
00124     this->resistor = resistor;
00125         
00126         //initizalize our status values
00127         this->steps_left = 0;
00128         this->direction = 0;
00129         
00130         //initialize register values
00131         driver_control_register_value=DRIVER_CONTROL_REGISTER | INITIAL_MICROSTEPPING;
00132         chopper_config_register=CHOPPER_CONFIG_REGISTER;
00133         
00134         //setting the default register values
00135         driver_control_register_value=DRIVER_CONTROL_REGISTER|INITIAL_MICROSTEPPING;
00136         microsteps = (1 << INITIAL_MICROSTEPPING);
00137         chopper_config_register=CHOPPER_CONFIG_REGISTER;
00138         cool_step_register_value=COOL_STEP_REGISTER;
00139         stall_guard2_current_register_value=STALL_GUARD2_LOAD_MEASURE_REGISTER;
00140         driver_configuration_register_value = DRIVER_CONFIG_REGISTER | READ_STALL_GUARD_READING;
00141 
00142         //set the current
00143         setCurrent(current);
00144         //set to a conservative start value
00145         setConstantOffTimeChopper(7, 54, 13,12,1);
00146     //set a nice microstepping value
00147     setMicrosteps(DEFAULT_MICROSTEPPING_VALUE);
00148     //save the number of steps
00149     this->number_of_steps =   number_of_steps;
00150 }
00151 
00152 
00153 /*
00154  * start & configure the stepper driver
00155  * just must be called.
00156  */
00157 void TMC26XStepper::start() {
00158 
00159 #ifdef DEBUG    
00160         Serial.println("TMC26X stepper library");
00161         Serial.print("CS pin: ");
00162         Serial.println(cs_pin);
00163         Serial.print("DIR pin: ");
00164         Serial.println(dir_pin);
00165         Serial.print("STEP pin: ");
00166         Serial.println(step_pin);
00167         Serial.print("current scaling: ");
00168         Serial.println(current_scaling,DEC);
00169 #endif
00170         //set the pins as output & its initial value
00171         pinMode(step_pin, OUTPUT);     
00172         pinMode(dir_pin, OUTPUT);     
00173         pinMode(cs_pin, OUTPUT);     
00174         digitalWrite(step_pin, LOW);     
00175         digitalWrite(dir_pin, LOW);     
00176         digitalWrite(cs_pin, HIGH);   
00177         
00178         //configure the SPI interface
00179     SPI.setBitOrder(MSBFIRST);
00180         SPI.setClockDivider(SPI_CLOCK_DIV8);
00181         //todo this does not work reliably - find a way to foolprof set it (e.g. while communicating
00182         //SPI.setDataMode(SPI_MODE3);
00183         SPI.begin();
00184                 
00185         //set the initial values
00186         send262(driver_control_register_value); 
00187         send262(chopper_config_register);
00188         send262(cool_step_register_value);
00189         send262(stall_guard2_current_register_value);
00190         send262(driver_configuration_register_value);
00191         
00192         //save that we are in running mode
00193         started=true;
00194 }
00195 
00196 /*
00197  Mark the driver as unstarted to be able to start it again
00198  */
00199 void TMC26XStepper::un_start() {
00200     started=false;
00201 }
00202 
00203 
00204 /*
00205   Sets the speed in revs per minute
00206 
00207 */
00208 void TMC26XStepper::setSpeed(unsigned int whatSpeed)
00209 {
00210   this->speed = whatSpeed;
00211   this->step_delay = (60UL * 1000UL * 1000UL) / ((unsigned long)this->number_of_steps * (unsigned long)whatSpeed * (unsigned long)this->microsteps);
00212 #ifdef DEBUG
00213     Serial.print("Step delay in micros: ");
00214     Serial.println(this->step_delay);
00215 #endif
00216     //update the next step time
00217     this->next_step_time = this->last_step_time+this->step_delay; 
00218 
00219 }
00220 
00221 unsigned int TMC26XStepper::getSpeed(void) {
00222     return this->speed;
00223 }
00224 
00225 /*
00226   Moves the motor steps_to_move steps.  If the number is negative, 
00227    the motor moves in the reverse direction.
00228  */
00229 char TMC26XStepper::step(int steps_to_move)
00230 {  
00231         if (this->steps_left==0) {
00232                 this->steps_left = abs(steps_to_move);  // how many steps to take
00233   
00234                 // determine direction based on whether steps_to_mode is + or -:
00235                 if (steps_to_move > 0) {
00236                         this->direction = 1;
00237                 } else if (steps_to_move < 0) {
00238                         this->direction = 0;
00239                 }
00240                 return 0;
00241     } else {
00242         return -1;
00243     }
00244 }
00245 
00246 char TMC26XStepper::move(void) {
00247   // decrement the number of steps, moving one step each time:
00248   if(this->steps_left>0) {
00249       unsigned long time = micros();  
00250           // move only if the appropriate delay has passed:
00251          if (time >= this->next_step_time) {
00252                 // increment or decrement the step number,
00253                 // depending on direction:
00254                 if (this->direction == 1) {
00255                         digitalWrite(step_pin, HIGH);
00256         } else { 
00257                   digitalWrite(dir_pin, HIGH);
00258                   digitalWrite(step_pin, HIGH);
00259             }
00260         // get the timeStamp of when you stepped:
00261         this->last_step_time = time;
00262         this->next_step_time = time+this->step_delay; 
00263         // decrement the steps left:
00264         steps_left--;
00265                 //disable the step & dir pins
00266                 digitalWrite(step_pin, LOW);
00267                 digitalWrite(dir_pin, LOW);
00268         }
00269         return -1;
00270         }
00271     return 0;
00272 }
00273 
00274 char TMC26XStepper::isMoving(void) {
00275         return (this->steps_left>0);
00276 }
00277 
00278 unsigned int TMC26XStepper::getStepsLeft(void) {
00279         return this->steps_left;
00280 }
00281 
00282 char TMC26XStepper::stop(void) {
00283         //note to self if the motor is currently moving
00284         char state = isMoving();
00285         //stop the motor
00286         this->steps_left = 0;
00287         this->direction = 0;
00288         //return if it was moving
00289         return state;
00290 }
00291 
00292 void TMC26XStepper::setCurrent(unsigned int current) {
00293     unsigned char current_scaling = 0;
00294         //calculate the current scaling from the max current setting (in mA)
00295         double mASetting = (double)current;
00296     double resistor_value = (double) this->resistor;
00297         // remove vesense flag
00298         this->driver_configuration_register_value &= ~(VSENSE); 
00299         //this is derrived from I=(cs+1)/32*(Vsense/Rsense)
00300     //leading to cs = CS = 32*R*I/V (with V = 0,31V oder 0,165V  and I = 1000*current)
00301         //with Rsense=0,15
00302         //for vsense = 0,310V (VSENSE not set)
00303         //or vsense = 0,165V (VSENSE set)
00304         current_scaling = (byte)((resistor_value*mASetting*32.0/(0.31*1000.0*1000.0))-0.5); //theoretically - 1.0 for better rounding it is 0.5
00305         
00306         //check if the current scalingis too low
00307         if (current_scaling<16) {
00308         //set the csense bit to get a use half the sense voltage (to support lower motor currents)
00309                 this->driver_configuration_register_value |= VSENSE;
00310         //and recalculate the current setting
00311         current_scaling = (byte)((resistor_value*mASetting*32.0/(0.165*1000.0*1000.0))-0.5); //theoretically - 1.0 for better rounding it is 0.5
00312 #ifdef DEBUG
00313                 Serial.print("CS (Vsense=1): ");
00314                 Serial.println(current_scaling);
00315         } else {
00316         Serial.print("CS: ");
00317         Serial.println(current_scaling);
00318 #endif
00319     }
00320 
00321         //do some sanity checks
00322         if (current_scaling>31) {
00323                 current_scaling=31;
00324         }
00325         //delete the old value
00326         stall_guard2_current_register_value &= ~(CURRENT_SCALING_PATTERN);
00327         //set the new current scaling
00328         stall_guard2_current_register_value |= current_scaling;
00329         //if started we directly send it to the motor
00330         if (started) {
00331         send262(driver_configuration_register_value);
00332                 send262(stall_guard2_current_register_value);
00333         }
00334 }
00335 
00336 unsigned int TMC26XStepper::getCurrent(void) {
00337     //we calculate the current according to the datasheet to be on the safe side
00338     //this is not the fastest but the most accurate and illustrative way
00339     double result = (double)(stall_guard2_current_register_value & CURRENT_SCALING_PATTERN);
00340     double resistor_value = (double)this->resistor;
00341     double voltage = (driver_configuration_register_value & VSENSE)? 0.165:0.31;
00342     result = (result+1.0)/32.0*voltage/resistor_value*1000.0*1000.0;
00343     return (unsigned int)result;
00344 }
00345 
00346 void TMC26XStepper::setStallGuardThreshold(char stall_guard_threshold, char stall_guard_filter_enabled) {
00347         if (stall_guard_threshold<-64) {
00348                 stall_guard_threshold = -64;
00349         //We just have 5 bits   
00350         } else if (stall_guard_threshold > 63) {
00351                 stall_guard_threshold = 63;
00352         }
00353         //add trim down to 7 bits
00354         stall_guard_threshold &=0x7f;
00355         //delete old stall guard settings
00356         stall_guard2_current_register_value &= ~(STALL_GUARD_CONFIG_PATTERN);
00357         if (stall_guard_filter_enabled) {
00358                 stall_guard2_current_register_value |= STALL_GUARD_FILTER_ENABLED;
00359         }
00360         //Set the new stall guard threshold
00361         stall_guard2_current_register_value |= (((unsigned long)stall_guard_threshold << 8) & STALL_GUARD_CONFIG_PATTERN);
00362         //if started we directly send it to the motor
00363         if (started) {
00364                 send262(stall_guard2_current_register_value);
00365         }
00366 }
00367 
00368 char TMC26XStepper::getStallGuardThreshold(void) {
00369     unsigned long stall_guard_threshold = stall_guard2_current_register_value & STALL_GUARD_VALUE_PATTERN;
00370     //shift it down to bit 0
00371     stall_guard_threshold >>=8;
00372     //convert the value to an int to correctly handle the negative numbers
00373     char result = stall_guard_threshold;
00374     //check if it is negative and fill it up with leading 1 for proper negative number representation
00375     if (result & _BV(6)) {
00376         result |= 0xC0;
00377     }
00378     return result;
00379 }
00380 
00381 char TMC26XStepper::getStallGuardFilter(void) {
00382     if (stall_guard2_current_register_value & STALL_GUARD_FILTER_ENABLED) {
00383         return -1;
00384     } else {
00385         return 0;
00386     }
00387 }
00388 /*
00389  * Set the number of microsteps per step.
00390  * 0,2,4,8,16,32,64,128,256 is supported
00391  * any value in between will be mapped to the next smaller value
00392  * 0 and 1 set the motor in full step mode
00393  */
00394 void TMC26XStepper::setMicrosteps(int number_of_steps) {
00395         long setting_pattern;
00396         //poor mans log
00397         if (number_of_steps>=256) {
00398                 setting_pattern=0;
00399                 microsteps=256;
00400         } else if (number_of_steps>=128) {
00401                 setting_pattern=1;
00402                 microsteps=128;
00403         } else if (number_of_steps>=64) {
00404                 setting_pattern=2;
00405                 microsteps=64;
00406         } else if (number_of_steps>=32) {
00407                 setting_pattern=3;
00408                 microsteps=32;
00409         } else if (number_of_steps>=16) {
00410                 setting_pattern=4;
00411                 microsteps=16;
00412         } else if (number_of_steps>=8) {
00413                 setting_pattern=5;
00414                 microsteps=8;
00415         } else if (number_of_steps>=4) {
00416                 setting_pattern=6;
00417                 microsteps=4;
00418         } else if (number_of_steps>=2) {
00419                 setting_pattern=7;
00420                 microsteps=2;
00421     //1 and 0 lead to full step
00422         } else if (number_of_steps<=1) {
00423                 setting_pattern=8;
00424                 microsteps=1;
00425         }
00426 #ifdef DEBUG
00427         Serial.print("Microstepping: ");
00428         Serial.println(microsteps);
00429 #endif
00430         //delete the old value
00431         this->driver_control_register_value &=0xFFFF0ul;
00432         //set the new value
00433         this->driver_control_register_value |=setting_pattern;
00434         
00435         //if started we directly send it to the motor
00436         if (started) {
00437                 send262(driver_control_register_value);
00438         }
00439     //recalculate the stepping delay by simply setting the speed again
00440     this->setSpeed(this->speed);
00441 }
00442 
00443 /*
00444  * returns the effective number of microsteps at the moment
00445  */
00446 int TMC26XStepper::getMicrosteps(void) {
00447         return microsteps;
00448 }
00449 
00450 /*
00451  * constant_off_time: The off time setting controls the minimum chopper frequency. 
00452  * For most applications an off time within     the range of 5μs to 20μs will fit.
00453  *              2...15: off time setting
00454  *
00455  * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the
00456  * duration of the ringing on the sense resistor. For
00457  *              0: min. setting 3: max. setting
00458  *
00459  * fast_decay_time_setting: Fast decay time setting. With CHM=1, these bits control the portion of fast decay for each chopper cycle.
00460  *              0: slow decay only
00461  *              1...15: duration of fast decay phase
00462  *
00463  * sine_wave_offset: Sine wave offset. With CHM=1, these bits control the sine wave offset. 
00464  * A positive offset corrects for zero crossing error.
00465  *              -3..-1: negative offset 0: no offset 1...12: positive offset
00466  *
00467  * use_current_comparator: Selects usage of the current comparator for termination of the fast decay cycle. 
00468  * If current comparator is enabled, it terminates the fast decay cycle in case the current 
00469  * reaches a higher negative value than the actual positive value.
00470  *              1: enable comparator termination of fast decay cycle
00471  *              0: end by time only
00472  */
00473 void TMC26XStepper::setConstantOffTimeChopper(char constant_off_time, char blank_time, char fast_decay_time_setting, char sine_wave_offset, unsigned char use_current_comparator) {
00474         //perform some sanity checks
00475         if (constant_off_time<2) {
00476                 constant_off_time=2;
00477         } else if (constant_off_time>15) {
00478                 constant_off_time=15;
00479         }
00480     //save the constant off time
00481     this->constant_off_time = constant_off_time;
00482         char blank_value;
00483         //calculate the value acc to the clock cycles
00484         if (blank_time>=54) {
00485                 blank_value=3;
00486         } else if (blank_time>=36) {
00487                 blank_value=2;
00488         } else if (blank_time>=24) {
00489                 blank_value=1;
00490         } else {
00491                 blank_value=0;
00492         }
00493         if (fast_decay_time_setting<0) {
00494                 fast_decay_time_setting=0;
00495         } else if (fast_decay_time_setting>15) {
00496                 fast_decay_time_setting=15;
00497         }
00498         if (sine_wave_offset < -3) {
00499                 sine_wave_offset = -3;
00500         } else if (sine_wave_offset>12) {
00501                 sine_wave_offset = 12;
00502         }
00503         //shift the sine_wave_offset
00504         sine_wave_offset +=3;
00505         
00506         //calculate the register setting
00507         //first of all delete all the values for this
00508         chopper_config_register &= ~((1<<12) | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN);
00509         //set the constant off pattern
00510         chopper_config_register |= CHOPPER_MODE_T_OFF_FAST_DECAY;
00511         //set the blank timing value
00512         chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT;
00513         //setting the constant off time
00514         chopper_config_register |= constant_off_time;
00515         //set the fast decay time
00516         //set msb
00517         chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x8))<<HYSTERESIS_DECREMENT_SHIFT);
00518         //other bits
00519         chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x7))<<HYSTERESIS_START_VALUE_SHIFT);
00520         //set the sine wave offset
00521         chopper_config_register |= (unsigned long)sine_wave_offset << HYSTERESIS_LOW_SHIFT;
00522         //using the current comparator?
00523         if (!use_current_comparator) {
00524                 chopper_config_register |= (1<<12);
00525         }
00526         //if started we directly send it to the motor
00527         if (started) {
00528                 send262(driver_control_register_value);
00529         }       
00530 }
00531 
00532 /*
00533  * constant_off_time: The off time setting controls the minimum chopper frequency. 
00534  * For most applications an off time within     the range of 5μs to 20μs will fit.
00535  *              2...15: off time setting
00536  *
00537  * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the
00538  * duration of the ringing on the sense resistor. For
00539  *              0: min. setting 3: max. setting
00540  *
00541  * hysteresis_start: Hysteresis start setting. Please remark, that this value is an offset to the hysteresis end value HEND.
00542  *              1...8
00543  *
00544  * hysteresis_end: Hysteresis end setting. Sets the hysteresis end value after a number of decrements. Decrement interval time is controlled by HDEC. 
00545  * The sum HSTRT+HEND must be <16. At a current setting CS of max. 30 (amplitude reduced to 240), the sum is not limited.
00546  *              -3..-1: negative HEND 0: zero HEND 1...12: positive HEND
00547  *
00548  * hysteresis_decrement: Hysteresis decrement setting. This setting determines the slope of the hysteresis during on time and during fast decay time.
00549  *              0: fast decrement 3: very slow decrement
00550  */
00551 
00552 void TMC26XStepper::setSpreadCycleChopper(char constant_off_time, char blank_time, char hysteresis_start, char hysteresis_end, char hysteresis_decrement) {
00553         //perform some sanity checks
00554         if (constant_off_time<2) {
00555                 constant_off_time=2;
00556         } else if (constant_off_time>15) {
00557                 constant_off_time=15;
00558         }
00559     //save the constant off time
00560     this->constant_off_time = constant_off_time;
00561         char blank_value;
00562         //calculate the value acc to the clock cycles
00563         if (blank_time>=54) {
00564                 blank_value=3;
00565         } else if (blank_time>=36) {
00566                 blank_value=2;
00567         } else if (blank_time>=24) {
00568                 blank_value=1;
00569         } else {
00570                 blank_value=0;
00571         }
00572         if (hysteresis_start<1) {
00573                 hysteresis_start=1;
00574         } else if (hysteresis_start>8) {
00575                 hysteresis_start=8;
00576         }
00577         hysteresis_start--;
00578 
00579         if (hysteresis_end < -3) {
00580                 hysteresis_end = -3;
00581         } else if (hysteresis_end>12) {
00582                 hysteresis_end = 12;
00583         }
00584         //shift the hysteresis_end
00585         hysteresis_end +=3;
00586 
00587         if (hysteresis_decrement<0) {
00588                 hysteresis_decrement=0;
00589         } else if (hysteresis_decrement>3) {
00590                 hysteresis_decrement=3;
00591         }
00592         
00593         //first of all delete all the values for this
00594         chopper_config_register &= ~(CHOPPER_MODE_T_OFF_FAST_DECAY | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN);
00595 
00596         //set the blank timing value
00597         chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT;
00598         //setting the constant off time
00599         chopper_config_register |= constant_off_time;
00600         //set the hysteresis_start
00601         chopper_config_register |= ((unsigned long)hysteresis_start) << HYSTERESIS_START_VALUE_SHIFT;
00602         //set the hysteresis end
00603         chopper_config_register |= ((unsigned long)hysteresis_end) << HYSTERESIS_LOW_SHIFT;
00604         //set the hystereis decrement
00605         chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT;
00606         //if started we directly send it to the motor
00607         if (started) {
00608                 send262(driver_control_register_value);
00609         }       
00610 }
00611 
00612 /*
00613  * In a constant off time chopper scheme both coil choppers run freely, i.e. are not synchronized. 
00614  * The frequency of each chopper mainly depends on the coil current and the position dependant motor coil inductivity, thus it depends on the microstep position. 
00615  * With some motors a slightly audible beat can occur between the chopper frequencies, especially when they are near to each other. This typically occurs at a 
00616  * few microstep positions within each quarter wave. This effect normally is not audible when compared to mechanical noise generated by ball bearings, etc. 
00617  * Further factors which can cause a similar effect are a poor layout of sense resistor GND connection.
00618  * Hint: A common factor, which can cause motor noise, is a bad PCB layout causing coupling of both sense resistor voltages 
00619  * (please refer to sense resistor layout hint in chapter 8.1).
00620  * In order to minimize the effect of a beat between both chopper frequencies, an internal random generator is provided. 
00621  * It modulates the slow decay time setting when switched on by the RNDTF bit. The RNDTF feature further spreads the chopper spectrum, 
00622  * reducing electromagnetic emission on single frequencies.
00623  */
00624 void TMC26XStepper::setRandomOffTime(char value) {
00625         if (value) {
00626                 chopper_config_register |= RANDOM_TOFF_TIME;
00627         } else {
00628                 chopper_config_register &= ~(RANDOM_TOFF_TIME);
00629         }
00630         //if started we directly send it to the motor
00631         if (started) {
00632                 send262(driver_control_register_value);
00633         }       
00634 }       
00635 
00636 void TMC26XStepper::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, unsigned char current_decrement_step_size,
00637                               unsigned char current_increment_step_size, unsigned char lower_current_limit) {
00638     //sanitize the input values
00639     if (lower_SG_threshold>480) {
00640         lower_SG_threshold = 480;
00641     }
00642     //divide by 32
00643     lower_SG_threshold >>=5;
00644     if (SG_hysteresis>480) {
00645         SG_hysteresis=480;
00646     }
00647     //divide by 32
00648     SG_hysteresis >>=5;
00649     if (current_decrement_step_size>3) {
00650         current_decrement_step_size=3;
00651     }
00652     if (current_increment_step_size>3) {
00653         current_increment_step_size=3;
00654     }
00655     if (lower_current_limit>1) {
00656         lower_current_limit=1;
00657     }
00658     //store the lower level in order to enable/disable the cool step
00659     this->cool_step_lower_threshold=lower_SG_threshold;
00660     //if cool step is not enabled we delete the lower value to keep it disabled
00661     if (!this->cool_step_enabled) {
00662         lower_SG_threshold=0;
00663     }
00664     //the good news is that we can start with a complete new cool step register value
00665     //and simply set the values in the register
00666     cool_step_register_value = ((unsigned long)lower_SG_threshold) | (((unsigned long)SG_hysteresis)<<8) | (((unsigned long)current_decrement_step_size)<<5)
00667         | (((unsigned long)current_increment_step_size)<<13) | (((unsigned long)lower_current_limit)<<15)
00668         //and of course we have to include the signature of the register
00669         | COOL_STEP_REGISTER;
00670     //Serial.println(cool_step_register_value,HEX);
00671     if (started) {
00672         send262(cool_step_register_value);
00673     }
00674 }
00675 
00676 void TMC26XStepper::setCoolStepEnabled(boolean enabled) {
00677     //simply delete the lower limit to disable the cool step
00678     cool_step_register_value &= ~SE_MIN_PATTERN;
00679     //and set it to the proper value if cool step is to be enabled
00680     if (enabled) {
00681         cool_step_register_value |=this->cool_step_lower_threshold;
00682     }
00683     //and save the enabled status
00684     this->cool_step_enabled = enabled;
00685     //save the register value
00686     if (started) {
00687         send262(cool_step_register_value);
00688     }
00689 }
00690 
00691 boolean TMC26XStepper::isCoolStepEnabled(void) {
00692     return this->cool_step_enabled;
00693 }
00694 
00695 unsigned int TMC26XStepper::getCoolStepLowerSgThreshold() {
00696     //we return our internally stored value - in order to provide the correct setting even if cool step is not enabled
00697     return this->cool_step_lower_threshold<<5;
00698 }
00699 
00700 unsigned int TMC26XStepper::getCoolStepUpperSgThreshold() {
00701     return (unsigned char)((cool_step_register_value & SE_MAX_PATTERN)>>8)<<5;
00702 }
00703 
00704 unsigned char TMC26XStepper::getCoolStepCurrentIncrementSize() {
00705     return (unsigned char)((cool_step_register_value & CURRENT_DOWN_STEP_SPEED_PATTERN)>>13);
00706 }
00707 
00708 unsigned char TMC26XStepper::getCoolStepNumberOfSGReadings() {
00709     return (unsigned char)((cool_step_register_value & SE_CURRENT_STEP_WIDTH_PATTERN)>>5);
00710 }
00711 
00712 unsigned char TMC26XStepper::getCoolStepLowerCurrentLimit() {
00713     return (unsigned char)((cool_step_register_value & MINIMUM_CURRENT_FOURTH)>>15);
00714 }
00715 
00716 void TMC26XStepper::setEnabled(boolean enabled) {
00717     //delete the t_off in the chopper config to get sure
00718     chopper_config_register &= ~(T_OFF_PATTERN);
00719     if (enabled) {
00720         //and set the t_off time
00721         chopper_config_register |= this->constant_off_time;
00722     }
00723     //if not enabled we don't have to do anything since we already delete t_off from the register
00724         if (started) {
00725                 send262(chopper_config_register);
00726         }       
00727 }
00728 
00729 boolean TMC26XStepper::isEnabled() {
00730     if (chopper_config_register & T_OFF_PATTERN) {
00731         return true;
00732     } else {
00733         return false;
00734     }
00735 }
00736 
00737 /*
00738  * reads a value from the TMC26X status register. The value is not obtained directly but can then 
00739  * be read by the various status routines.
00740  *
00741  */
00742 void TMC26XStepper::readStatus(char read_value) {
00743     unsigned long old_driver_configuration_register_value = driver_configuration_register_value;
00744         //reset the readout configuration
00745         driver_configuration_register_value &= ~(READ_SELECTION_PATTERN);
00746         //this now equals TMC26X_READOUT_POSITION - so we just have to check the other two options
00747         if (read_value == TMC26X_READOUT_STALLGUARD) {
00748                 driver_configuration_register_value |= READ_STALL_GUARD_READING;
00749         } else if (read_value == TMC26X_READOUT_CURRENT) {
00750                 driver_configuration_register_value |= READ_STALL_GUARD_AND_COOL_STEP;
00751         }
00752         //all other cases are ignored to prevent funny values
00753     //check if the readout is configured for the value we are interested in
00754     if (driver_configuration_register_value!=old_driver_configuration_register_value) {
00755             //because then we need to write the value twice - one time for configuring, second time to get the value, see below
00756             send262(driver_configuration_register_value);
00757         }
00758     //write the configuration to get the last status    
00759         send262(driver_configuration_register_value);
00760 }
00761 
00762 int TMC26XStepper::getMotorPosition(void) {
00763         //we read it out even if we are not started yet - perhaps it is useful information for somebody
00764         readStatus(TMC26X_READOUT_POSITION);
00765     return getReadoutValue();
00766 }
00767 
00768 //reads the stall guard setting from last status
00769 //returns -1 if stallguard information is not present
00770 int TMC26XStepper::getCurrentStallGuardReading(void) {
00771         //if we don't yet started there cannot be a stall guard value
00772         if (!started) {
00773                 return -1;
00774         }
00775         //not time optimal, but solution optiomal:
00776         //first read out the stall guard value
00777         readStatus(TMC26X_READOUT_STALLGUARD);
00778         return getReadoutValue();
00779 }
00780 
00781 unsigned char TMC26XStepper::getCurrentCSReading(void) {
00782         //if we don't yet started there cannot be a stall guard value
00783         if (!started) {
00784                 return 0;
00785         }
00786         //not time optimal, but solution optiomal:
00787         //first read out the stall guard value
00788         readStatus(TMC26X_READOUT_CURRENT);
00789         return (getReadoutValue() & 0x1f);
00790 }
00791 
00792 unsigned int TMC26XStepper::getCurrentCurrent(void) {
00793     double result = (double)getCurrentCSReading();
00794     double resistor_value = (double)this->resistor;
00795     double voltage = (driver_configuration_register_value & VSENSE)? 0.165:0.31;
00796     result = (result+1.0)/32.0*voltage/resistor_value*1000.0*1000.0;
00797     return (unsigned int)result;
00798 }
00799 
00800 /*
00801  return true if the stallguard threshold has been reached
00802 */
00803 boolean TMC26XStepper::isStallGuardOverThreshold(void) {
00804         if (!this->started) {
00805                 return false;
00806         }
00807         return (driver_status_result & STATUS_STALL_GUARD_STATUS);
00808 }
00809 
00810 /*
00811  returns if there is any over temperature condition:
00812  OVER_TEMPERATURE_PREWARING if pre warning level has been reached
00813  OVER_TEMPERATURE_SHUTDOWN if the temperature is so hot that the driver is shut down
00814  Any of those levels are not too good.
00815 */
00816 char TMC26XStepper::getOverTemperature(void) {
00817         if (!this->started) {
00818                 return 0;
00819         }
00820         if (driver_status_result & STATUS_OVER_TEMPERATURE_SHUTDOWN) {
00821                 return TMC26X_OVERTEMPERATURE_SHUTDOWN;
00822         }
00823         if (driver_status_result & STATUS_OVER_TEMPERATURE_WARNING) {
00824                 return TMC26X_OVERTEMPERATURE_PREWARING;
00825         }
00826         return 0;
00827 }
00828 
00829 //is motor channel A shorted to ground
00830 boolean TMC26XStepper::isShortToGroundA(void) {
00831         if (!this->started) {
00832                 return false;
00833         }
00834         return (driver_status_result & STATUS_SHORT_TO_GROUND_A);
00835 }
00836 
00837 //is motor channel B shorted to ground
00838 boolean TMC26XStepper::isShortToGroundB(void) {
00839         if (!this->started) {
00840                 return false;
00841         }
00842         return (driver_status_result & STATUS_SHORT_TO_GROUND_B);
00843 }
00844 
00845 //is motor channel A connected
00846 boolean TMC26XStepper::isOpenLoadA(void) {
00847         if (!this->started) {
00848                 return false;
00849         }
00850         return (driver_status_result & STATUS_OPEN_LOAD_A);
00851 }
00852 
00853 //is motor channel B connected
00854 boolean TMC26XStepper::isOpenLoadB(void) {
00855         if (!this->started) {
00856                 return false;
00857         }
00858         return (driver_status_result & STATUS_OPEN_LOAD_B);
00859 }
00860 
00861 //is chopper inactive since 2^20 clock cycles - defaults to ~0,08s
00862 boolean TMC26XStepper::isStandStill(void) {
00863         if (!this->started) {
00864                 return false;
00865         }
00866         return (driver_status_result & STATUS_STAND_STILL);
00867 }
00868 
00869 //is chopper inactive since 2^20 clock cycles - defaults to ~0,08s
00870 boolean TMC26XStepper::isStallGuardReached(void) {
00871         if (!this->started) {
00872                 return false;
00873         }
00874         return (driver_status_result & STATUS_STALL_GUARD_STATUS);
00875 }
00876 
00877 //reads the stall guard setting from last status
00878 //returns -1 if stallguard inforamtion is not present
00879 int TMC26XStepper::getReadoutValue(void) {
00880         return (int)(driver_status_result >> 10);
00881 }
00882 
00883 int TMC26XStepper::getResistor() {
00884     return this->resistor;
00885 }
00886 
00887 boolean TMC26XStepper::isCurrentScalingHalfed() {
00888     if (this->driver_configuration_register_value & VSENSE) {
00889         return true;
00890     } else {
00891         return false;
00892     }
00893 }
00894 /*
00895  version() returns the version of the library:
00896  */
00897 int TMC26XStepper::version(void)
00898 {
00899         return 1;
00900 }
00901 
00902 void TMC26XStepper::debugLastStatus() {
00903 #ifdef DEBUG    
00904 if (this->started) {
00905                 if (this->getOverTemperature()&TMC26X_OVERTEMPERATURE_PREWARING) {
00906                         Serial.println("WARNING: Overtemperature Prewarning!");
00907                 } else if (this->getOverTemperature()&TMC26X_OVERTEMPERATURE_SHUTDOWN) {
00908                         Serial.println("ERROR: Overtemperature Shutdown!");
00909                 }
00910                 if (this->isShortToGroundA()) {
00911                         Serial.println("ERROR: SHORT to ground on channel A!");
00912                 }
00913                 if (this->isShortToGroundB()) {
00914                         Serial.println("ERROR: SHORT to ground on channel A!");
00915                 }
00916                 if (this->isOpenLoadA()) {
00917                         Serial.println("ERROR: Channel A seems to be unconnected!");
00918                 }
00919                 if (this->isOpenLoadB()) {
00920                         Serial.println("ERROR: Channel B seems to be unconnected!");
00921                 }
00922                 if (this->isStallGuardReached()) {      
00923                         Serial.println("INFO: Stall Guard level reached!");
00924                 }
00925                 if (this->isStandStill()) {
00926                         Serial.println("INFO: Motor is standing still.");
00927                 }
00928                 unsigned long readout_config = driver_configuration_register_value & READ_SELECTION_PATTERN;
00929                 int value = getReadoutValue();
00930                 if (readout_config == READ_MICROSTEP_POSTION) {
00931                         Serial.print("Microstep postion phase A: ");
00932                         Serial.println(value);
00933                 } else if (readout_config == READ_STALL_GUARD_READING) {
00934                         Serial.print("Stall Guard value:");
00935                         Serial.println(value);
00936                 } else if (readout_config == READ_STALL_GUARD_AND_COOL_STEP) {
00937                         int stallGuard = value & 0xf;
00938                         int current = value & 0x1F0;
00939                         Serial.print("Approx Stall Guard: ");
00940                         Serial.println(stallGuard);
00941                         Serial.print("Current level");
00942                         Serial.println(current);
00943                 }
00944         }
00945 #endif
00946 }
00947 
00948 /*
00949  * send register settings to the stepper driver via SPI
00950  * returns the current status
00951  */
00952 inline void TMC26XStepper::send262(unsigned long datagram) {
00953         unsigned long i_datagram;
00954     
00955     //preserver the previous spi mode
00956     unsigned char oldMode =  SPCR & SPI_MODE_MASK;
00957         
00958     //if the mode is not correct set it to mode 3
00959     if (oldMode != SPI_MODE3) {
00960         SPI.setDataMode(SPI_MODE3);
00961     }
00962         
00963         //select the TMC driver
00964         digitalWrite(cs_pin,LOW);
00965 
00966         //ensure that only valid bist are set (0-19)
00967         //datagram &=REGISTER_BIT_PATTERN;
00968         
00969 #ifdef DEBUG
00970         Serial.print("Sending ");
00971         Serial.println(datagram,HEX);
00972 #endif
00973 
00974         //write/read the values
00975         i_datagram = SPI.transfer((datagram >> 16) & 0xff);
00976         i_datagram <<= 8;
00977         i_datagram |= SPI.transfer((datagram >>  8) & 0xff);
00978         i_datagram <<= 8;
00979         i_datagram |= SPI.transfer((datagram) & 0xff);
00980         i_datagram >>= 4;
00981         
00982 #ifdef DEBUG
00983         Serial.print("Received ");
00984         Serial.println(i_datagram,HEX);
00985         debugLastStatus();
00986 #endif
00987         //deselect the TMC chip
00988         digitalWrite(cs_pin,HIGH); 
00989     
00990     //restore the previous SPI mode if neccessary
00991     //if the mode is not correct set it to mode 3
00992     if (oldMode != SPI_MODE3) {
00993         SPI.setDataMode(oldMode);
00994     }
00995 
00996         
00997         //store the datagram as status result
00998         driver_status_result = i_datagram;
00999 }