Trinamic TMC26X Stepper Driver for Arduino
|
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 }