Trinamic TMC26X Stepper Driver for Arduino

- Main Page
- Classes
- Files

- File List
- File Members

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 }