added sample TMC26X spresadcycle setting
[clinton/Smoothieware.git] / src / modules / utils / motordrivercontrol / drivers / TMC26X / TMC26X.cpp
1 /*
2 Highly modifed from....
3
4 TMC26X.cpp - - TMC26X Stepper library for Wiring/Arduino
5
6 based on the stepper library by Tom Igoe, et. al.
7
8 Copyright (c) 2011, Interactive Matter, Marcus Nowotny
9
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files (the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
16
17 The above copyright notice and this permission notice shall be included in
18 all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 THE SOFTWARE.
27
28 */
29
30 #include "TMC26X.h"
31 #include "mbed.h"
32 #include "StreamOutput.h"
33 #include "Kernel.h"
34 #include "libs/StreamOutputPool.h"
35 #include "Robot.h"
36 #include "StepperMotor.h"
37 #include "ConfigValue.h"
38 #include "Config.h"
39 #include "checksumm.h"
40 #include "StepTicker.h"
41
42 #define motor_driver_control_checksum CHECKSUM("motor_driver_control")
43 #define sense_resistor_checksum CHECKSUM("sense_resistor")
44
45 //! return value for TMC26X.getOverTemperature() if there is a overtemperature situation in the TMC chip
46 /*!
47 * This warning indicates that the TCM chip is too warm.
48 * It is still working but some parameters may be inferior.
49 * You should do something against it.
50 */
51 #define TMC26X_OVERTEMPERATURE_PREWARING 1
52 //! return value for TMC26X.getOverTemperature() if there is a overtemperature shutdown in the TMC chip
53 /*!
54 * This warning indicates that the TCM chip is too warm to operate and has shut down to prevent damage.
55 * It will stop working until it cools down again.
56 * If you encouter this situation you must do something against it. Like reducing the current or improving the PCB layout
57 * and/or heat management.
58 */
59 #define TMC26X_OVERTEMPERATURE_SHUTDOWN 2
60
61 //which values can be read out
62 /*!
63 * Selects to readout the microstep position from the motor.
64 *\sa readStatus()
65 */
66 #define TMC26X_READOUT_POSITION 0
67 /*!
68 * Selects to read out the StallGuard value of the motor.
69 *\sa readStatus()
70 */
71 #define TMC26X_READOUT_STALLGUARD 1
72 /*!
73 * Selects to read out the current current setting (acc. to CoolStep) and the upper bits of the StallGuard value from the motor.
74 *\sa readStatus(), setCurrent()
75 */
76 #define TMC26X_READOUT_CURRENT 3
77
78 /*!
79 * Define to set the minimum current for CoolStep operation to 1/2 of the selected CS minium.
80 *\sa setCoolStepConfiguration()
81 */
82 #define COOL_STEP_HALF_CS_LIMIT 0
83 /*!
84 * Define to set the minimum current for CoolStep operation to 1/4 of the selected CS minium.
85 *\sa setCoolStepConfiguration()
86 */
87 #define COOL_STEP_QUARTDER_CS_LIMIT 1
88
89
90 //some default values used in initialization
91 #define DEFAULT_MICROSTEPPING_VALUE 32
92
93 //TMC26X register definitions
94 #define DRIVER_CONTROL_REGISTER 0x00000ul
95 #define CHOPPER_CONFIG_REGISTER 0x80000ul
96 #define COOL_STEP_REGISTER 0xA0000ul
97 #define STALL_GUARD2_LOAD_MEASURE_REGISTER 0xC0000ul
98 #define DRIVER_CONFIG_REGISTER 0xE0000ul
99
100 #define REGISTER_BIT_PATTERN 0xFFFFFul
101
102 //definitions for the driver control register DRVCTL
103 #define MICROSTEPPING_PATTERN 0x000Ful
104 #define STEP_INTERPOLATION 0x0200ul
105 #define DOUBLE_EDGE_STEP 0x0100ul
106
107 //definitions for the driver config register DRVCONF
108 #define READ_MICROSTEP_POSITION 0x0000ul
109 #define READ_STALL_GUARD_READING 0x0010ul
110 #define READ_STALL_GUARD_AND_COOL_STEP 0x0020ul
111 #define READ_SELECTION_PATTERN 0x0030ul
112 #define VSENSE 0x0040ul
113
114 //definitions for the chopper config register
115 #define CHOPPER_MODE_STANDARD 0x00000ul
116 #define CHOPPER_MODE_T_OFF_FAST_DECAY 0x04000ul
117 #define T_OFF_PATTERN 0x0000ful
118 #define RANDOM_TOFF_TIME 0x02000ul
119 #define BLANK_TIMING_PATTERN 0x18000ul
120 #define BLANK_TIMING_SHIFT 15
121 #define HYSTERESIS_DECREMENT_PATTERN 0x01800ul
122 #define HYSTERESIS_DECREMENT_SHIFT 11
123 #define HYSTERESIS_LOW_VALUE_PATTERN 0x00780ul
124 #define HYSTERESIS_LOW_SHIFT 7
125 #define HYSTERESIS_START_VALUE_PATTERN 0x00078ul
126 #define HYSTERESIS_START_VALUE_SHIFT 4
127 #define T_OFF_TIMING_PATERN 0x0000Ful
128
129 //definitions for cool step register
130 #define MINIMUM_CURRENT_FOURTH 0x8000ul
131 #define CURRENT_DOWN_STEP_SPEED_PATTERN 0x6000ul
132 #define SE_MAX_PATTERN 0x0F00ul
133 #define SE_CURRENT_STEP_WIDTH_PATTERN 0x0060ul
134 #define SE_MIN_PATTERN 0x000Ful
135
136 //definitions for stall guard2 current register
137 #define STALL_GUARD_FILTER_ENABLED 0x10000ul
138 #define STALL_GUARD_TRESHHOLD_VALUE_PATTERN 0x17F00ul
139 #define CURRENT_SCALING_PATTERN 0x0001Ful
140 #define STALL_GUARD_CONFIG_PATTERN 0x17F00ul
141 #define STALL_GUARD_VALUE_PATTERN 0x07F00ul
142
143 //definitions for the input from the TCM260
144 #define STATUS_STALL_GUARD_STATUS 0x00001ul
145 #define STATUS_OVER_TEMPERATURE_SHUTDOWN 0x00002ul
146 #define STATUS_OVER_TEMPERATURE_WARNING 0x00004ul
147 #define STATUS_SHORT_TO_GROUND_A 0x00008ul
148 #define STATUS_SHORT_TO_GROUND_B 0x00010ul
149 #define STATUS_OPEN_LOAD_A 0x00020ul
150 #define STATUS_OPEN_LOAD_B 0x00040ul
151 #define STATUS_STAND_STILL 0x00080ul
152 #define READOUT_VALUE_PATTERN 0xFFC00ul
153
154 //debuging output
155 //#define DEBUG
156
157 /*
158 * Constructor
159 */
160 TMC26X::TMC26X(std::function<int(uint8_t *b, int cnt, uint8_t *r)> spi, char d) : spi(spi), designator(d)
161 {
162 //we are not started yet
163 started = false;
164 //by default cool step is not enabled
165 cool_step_enabled = false;
166 error_reported.reset();
167 }
168
169 /*
170 * configure the stepper driver
171 * just must be called.
172 */
173 void TMC26X::init(uint16_t cs)
174 {
175 // read chip specific config entries
176 this->resistor= THEKERNEL->config->value(motor_driver_control_checksum, cs, sense_resistor_checksum)->by_default(50)->as_number(); // in milliohms
177
178 //setting the default register values
179 driver_control_register_value = DRIVER_CONTROL_REGISTER;
180 chopper_config_register = CHOPPER_CONFIG_REGISTER;
181 cool_step_register_value = COOL_STEP_REGISTER;
182 stall_guard2_current_register_value = STALL_GUARD2_LOAD_MEASURE_REGISTER;
183 driver_configuration_register_value = DRIVER_CONFIG_REGISTER | READ_STALL_GUARD_READING;
184
185 //set the initial values
186 send262(driver_control_register_value);
187 send262(chopper_config_register);
188 send262(cool_step_register_value);
189 send262(stall_guard2_current_register_value);
190 send262(driver_configuration_register_value);
191
192 started = true;
193
194 #if 1
195 //set to a conservative start value
196 setConstantOffTimeChopper(7, 54, 13, 12, 1);
197 #else
198 //void TMC26X::setSpreadCycleChopper( constant_off_time, blank_time, hysteresis_start, hysteresis_end, hysteresis_decrement);
199
200 // openbuilds high torque nema23 3amps (2.8)
201 setSpreadCycleChopper(5, 36, 6, 0, 0);
202 // for 1.5amp kysan @ 12v
203 setSpreadCycleChopper(5, 54, 5, 0, 0);
204 // for 4amp Nema24 @ 12v
205 //setSpreadCycleChopper(5, 54, 4, 0, 0);
206 #endif
207
208 setEnabled(false);
209
210 //set a nice microstepping value
211 setMicrosteps(DEFAULT_MICROSTEPPING_VALUE);
212
213 // set stallguard to a conservative value so it doesn't trigger immediately
214 setStallGuardThreshold(10, 1);
215 }
216
217 void TMC26X::setCurrent(unsigned int current)
218 {
219 uint8_t current_scaling = 0;
220 //calculate the current scaling from the max current setting (in mA)
221 double mASetting = (double)current;
222 double resistor_value = (double) this->resistor;
223 // remove vesense flag
224 this->driver_configuration_register_value &= ~(VSENSE);
225 //this is derrived from I=(cs+1)/32*(Vsense/Rsense)
226 //leading to cs = CS = 32*R*I/V (with V = 0,31V oder 0,165V and I = 1000*current)
227 //with Rsense=0,15
228 //for vsense = 0,310V (VSENSE not set)
229 //or vsense = 0,165V (VSENSE set)
230 current_scaling = (uint8_t)((resistor_value * mASetting * 32.0F / (0.31F * 1000.0F * 1000.0F)) - 0.5F); //theoretically - 1.0 for better rounding it is 0.5
231
232 //check if the current scaling is too low
233 if (current_scaling < 16) {
234 //set the csense bit to get a use half the sense voltage (to support lower motor currents)
235 this->driver_configuration_register_value |= VSENSE;
236 //and recalculate the current setting
237 current_scaling = (uint8_t)((resistor_value * mASetting * 32.0F / (0.165F * 1000.0F * 1000.0F)) - 0.5F); //theoretically - 1.0 for better rounding it is 0.5
238 }
239
240 //do some sanity checks
241 if (current_scaling > 31) {
242 current_scaling = 31;
243 }
244 //delete the old value
245 stall_guard2_current_register_value &= ~(CURRENT_SCALING_PATTERN);
246 //set the new current scaling
247 stall_guard2_current_register_value |= current_scaling;
248 //if started we directly send it to the motor
249 if (started) {
250 send262(driver_configuration_register_value);
251 send262(stall_guard2_current_register_value);
252 }
253 }
254
255 unsigned int TMC26X::getCurrent(void)
256 {
257 //we calculate the current according to the datasheet to be on the safe side
258 //this is not the fastest but the most accurate and illustrative way
259 double result = (double)(stall_guard2_current_register_value & CURRENT_SCALING_PATTERN);
260 double resistor_value = (double)this->resistor;
261 double voltage = (driver_configuration_register_value & VSENSE) ? 0.165F : 0.31F;
262 result = (result + 1.0F) / 32.0F * voltage / resistor_value * 1000.0F * 1000.0F;
263 return (unsigned int)result;
264 }
265
266 void TMC26X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled)
267 {
268 if (stall_guard_threshold < -64) {
269 stall_guard_threshold = -64;
270 //We just have 5 bits
271 } else if (stall_guard_threshold > 63) {
272 stall_guard_threshold = 63;
273 }
274 //add trim down to 7 bits
275 stall_guard_threshold &= 0x7f;
276 //delete old stall guard settings
277 stall_guard2_current_register_value &= ~(STALL_GUARD_CONFIG_PATTERN);
278 if (stall_guard_filter_enabled) {
279 stall_guard2_current_register_value |= STALL_GUARD_FILTER_ENABLED;
280 }
281 //Set the new stall guard threshold
282 stall_guard2_current_register_value |= (((unsigned long)stall_guard_threshold << 8) & STALL_GUARD_CONFIG_PATTERN);
283 //if started we directly send it to the motor
284 if (started) {
285 send262(stall_guard2_current_register_value);
286 }
287 }
288
289 int8_t TMC26X::getStallGuardThreshold(void)
290 {
291 unsigned long stall_guard_threshold = stall_guard2_current_register_value & STALL_GUARD_VALUE_PATTERN;
292 //shift it down to bit 0
293 stall_guard_threshold >>= 8;
294 //convert the value to an int to correctly handle the negative numbers
295 int8_t result = stall_guard_threshold;
296 //check if it is negative and fill it up with leading 1 for proper negative number representation
297 if (result & (1 << 6)) {
298 result |= 0xC0;
299 }
300 return result;
301 }
302
303 int8_t TMC26X::getStallGuardFilter(void)
304 {
305 if (stall_guard2_current_register_value & STALL_GUARD_FILTER_ENABLED) {
306 return -1;
307 } else {
308 return 0;
309 }
310 }
311 /*
312 * Set the number of microsteps per step.
313 * 0,2,4,8,16,32,64,128,256 is supported
314 * any value in between will be mapped to the next smaller value
315 * 0 and 1 set the motor in full step mode
316 */
317 void TMC26X::setMicrosteps(int number_of_steps)
318 {
319 long setting_pattern;
320 //poor mans log
321 if (number_of_steps >= 256) {
322 setting_pattern = 0;
323 microsteps = 256;
324 } else if (number_of_steps >= 128) {
325 setting_pattern = 1;
326 microsteps = 128;
327 } else if (number_of_steps >= 64) {
328 setting_pattern = 2;
329 microsteps = 64;
330 } else if (number_of_steps >= 32) {
331 setting_pattern = 3;
332 microsteps = 32;
333 } else if (number_of_steps >= 16) {
334 setting_pattern = 4;
335 microsteps = 16;
336 } else if (number_of_steps >= 8) {
337 setting_pattern = 5;
338 microsteps = 8;
339 } else if (number_of_steps >= 4) {
340 setting_pattern = 6;
341 microsteps = 4;
342 } else if (number_of_steps >= 2) {
343 setting_pattern = 7;
344 microsteps = 2;
345 //1 and 0 lead to full step
346 } else if (number_of_steps <= 1) {
347 setting_pattern = 8;
348 microsteps = 1;
349 }
350
351 //delete the old value
352 this->driver_control_register_value &= 0xFFFF0ul;
353 //set the new value
354 this->driver_control_register_value |= setting_pattern;
355
356 //if started we directly send it to the motor
357 if (started) {
358 send262(driver_control_register_value);
359 }
360 }
361
362 /*
363 * returns the effective number of microsteps at the moment
364 */
365 int TMC26X::getMicrosteps(void)
366 {
367 return microsteps;
368 }
369
370 void TMC26X::setStepInterpolation(int8_t value)
371 {
372 if (value) {
373 driver_control_register_value |= STEP_INTERPOLATION;
374 } else {
375 driver_control_register_value &= ~(STEP_INTERPOLATION);
376 }
377 //if started we directly send it to the motor
378 if (started) {
379 send262(driver_control_register_value);
380 }
381 }
382
383 void TMC26X::setDoubleEdge(int8_t value)
384 {
385 if (value) {
386 driver_control_register_value |= DOUBLE_EDGE_STEP;
387 } else {
388 driver_control_register_value &= ~(DOUBLE_EDGE_STEP);
389 }
390 //if started we directly send it to the motor
391 if (started) {
392 send262(driver_control_register_value);
393 }
394 }
395
396 /*
397 * constant_off_time: The off time setting controls the minimum chopper frequency.
398 * For most applications an off time within the range of 5μs to 20μs will fit.
399 * 2...15: off time setting
400 *
401 * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the
402 * duration of the ringing on the sense resistor. For
403 * 0: min. setting 3: max. setting
404 *
405 * fast_decay_time_setting: Fast decay time setting. With CHM=1, these bits control the portion of fast decay for each chopper cycle.
406 * 0: slow decay only
407 * 1...15: duration of fast decay phase
408 *
409 * sine_wave_offset: Sine wave offset. With CHM=1, these bits control the sine wave offset.
410 * A positive offset corrects for zero crossing error.
411 * -3..-1: negative offset 0: no offset 1...12: positive offset
412 *
413 * use_current_comparator: Selects usage of the current comparator for termination of the fast decay cycle.
414 * If current comparator is enabled, it terminates the fast decay cycle in case the current
415 * reaches a higher negative value than the actual positive value.
416 * 1: enable comparator termination of fast decay cycle
417 * 0: end by time only
418 */
419 void TMC26X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator)
420 {
421 //perform some sanity checks
422 if (constant_off_time < 2) {
423 constant_off_time = 2;
424 } else if (constant_off_time > 15) {
425 constant_off_time = 15;
426 }
427 //save the constant off time
428 this->constant_off_time = constant_off_time;
429 int8_t blank_value;
430 //calculate the value acc to the clock cycles
431 if (blank_time >= 54) {
432 blank_value = 3;
433 } else if (blank_time >= 36) {
434 blank_value = 2;
435 } else if (blank_time >= 24) {
436 blank_value = 1;
437 } else {
438 blank_value = 0;
439 }
440 this->blank_time = blank_time;
441
442 if (fast_decay_time_setting < 0) {
443 fast_decay_time_setting = 0;
444 } else if (fast_decay_time_setting > 15) {
445 fast_decay_time_setting = 15;
446 }
447 if (sine_wave_offset < -3) {
448 sine_wave_offset = -3;
449 } else if (sine_wave_offset > 12) {
450 sine_wave_offset = 12;
451 }
452 //shift the sine_wave_offset
453 sine_wave_offset += 3;
454
455 //calculate the register setting
456 //first of all delete all the values for this
457 chopper_config_register &= ~((1 << 12) | BLANK_TIMING_PATTERN | HYSTERESIS_DECREMENT_PATTERN | HYSTERESIS_LOW_VALUE_PATTERN | HYSTERESIS_START_VALUE_PATTERN | T_OFF_TIMING_PATERN);
458 //set the constant off pattern
459 chopper_config_register |= CHOPPER_MODE_T_OFF_FAST_DECAY;
460 //set the blank timing value
461 chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT;
462 //setting the constant off time
463 chopper_config_register |= constant_off_time;
464 //set the fast decay time
465 //set msb
466 chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x8)) << HYSTERESIS_DECREMENT_SHIFT);
467 //other bits
468 chopper_config_register |= (((unsigned long)(fast_decay_time_setting & 0x7)) << HYSTERESIS_START_VALUE_SHIFT);
469 //set the sine wave offset
470 chopper_config_register |= (unsigned long)sine_wave_offset << HYSTERESIS_LOW_SHIFT;
471 //using the current comparator?
472 if (!use_current_comparator) {
473 chopper_config_register |= (1 << 12);
474 }
475 //if started we directly send it to the motor
476 if (started) {
477 send262(chopper_config_register);
478 }
479 }
480
481 /*
482 * constant_off_time: The off time setting controls the minimum chopper frequency.
483 * For most applications an off time within the range of 5μs to 20μs will fit.
484 * 2...15: off time setting
485 *
486 * blank_time: Selects the comparator blank time. This time needs to safely cover the switching event and the
487 * duration of the ringing on the sense resistor. For
488 * 0: min. setting 3: max. setting
489 *
490 * hysteresis_start: Hysteresis start setting. Please remark, that this value is an offset to the hysteresis end value HEND.
491 * 1...8
492 *
493 * hysteresis_end: Hysteresis end setting. Sets the hysteresis end value after a number of decrements. Decrement interval time is controlled by HDEC.
494 * The sum HSTRT+HEND must be <16. At a current setting CS of max. 30 (amplitude reduced to 240), the sum is not limited.
495 * -3..-1: negative HEND 0: zero HEND 1...12: positive HEND
496 *
497 * hysteresis_decrement: Hysteresis decrement setting. This setting determines the slope of the hysteresis during on time and during fast decay time.
498 * 0: fast decrement 3: very slow decrement
499 */
500
501 void TMC26X::setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement)
502 {
503 h_start = hysteresis_start;
504 h_end = hysteresis_end;
505 h_decrement = hysteresis_decrement;
506 this->blank_time = blank_time;
507
508 //perform some sanity checks
509 if (constant_off_time < 2) {
510 constant_off_time = 2;
511 } else if (constant_off_time > 15) {
512 constant_off_time = 15;
513 }
514 //save the constant off time
515 this->constant_off_time = constant_off_time;
516 int8_t blank_value;
517 //calculate the value acc to the clock cycles
518 if (blank_time >= 54) {
519 blank_value = 3;
520 } else if (blank_time >= 36) {
521 blank_value = 2;
522 } else if (blank_time >= 24) {
523 blank_value = 1;
524 } else {
525 blank_value = 0;
526 }
527
528 if (hysteresis_start < 1) {
529 hysteresis_start = 1;
530 } else if (hysteresis_start > 8) {
531 hysteresis_start = 8;
532 }
533 hysteresis_start--;
534
535 if (hysteresis_end < -3) {
536 hysteresis_end = -3;
537 } else if (hysteresis_end > 12) {
538 hysteresis_end = 12;
539 }
540 //shift the hysteresis_end
541 hysteresis_end += 3;
542
543 if (hysteresis_decrement < 0) {
544 hysteresis_decrement = 0;
545 } else if (hysteresis_decrement > 3) {
546 hysteresis_decrement = 3;
547 }
548
549 //first of all delete all the values for this
550 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);
551
552 //set the blank timing value
553 chopper_config_register |= ((unsigned long)blank_value) << BLANK_TIMING_SHIFT;
554 //setting the constant off time
555 chopper_config_register |= constant_off_time;
556 //set the hysteresis_start
557 chopper_config_register |= ((unsigned long)hysteresis_start) << HYSTERESIS_START_VALUE_SHIFT;
558 //set the hysteresis end
559 chopper_config_register |= ((unsigned long)hysteresis_end) << HYSTERESIS_LOW_SHIFT;
560 //set the hystereis decrement
561 chopper_config_register |= ((unsigned long)hysteresis_decrement) << HYSTERESIS_DECREMENT_SHIFT;
562
563 //if started we directly send it to the motor
564 if (started) {
565 send262(chopper_config_register);
566 }
567 }
568
569 /*
570 * In a constant off time chopper scheme both coil choppers run freely, i.e. are not synchronized.
571 * 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.
572 * 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
573 * few microstep positions within each quarter wave. This effect normally is not audible when compared to mechanical noise generated by ball bearings, etc.
574 * Further factors which can cause a similar effect are a poor layout of sense resistor GND connection.
575 * Hint: A common factor, which can cause motor noise, is a bad PCB layout causing coupling of both sense resistor voltages
576 * (please refer to sense resistor layout hint in chapter 8.1).
577 * In order to minimize the effect of a beat between both chopper frequencies, an internal random generator is provided.
578 * It modulates the slow decay time setting when switched on by the RNDTF bit. The RNDTF feature further spreads the chopper spectrum,
579 * reducing electromagnetic emission on single frequencies.
580 */
581 void TMC26X::setRandomOffTime(int8_t value)
582 {
583 if (value) {
584 chopper_config_register |= RANDOM_TOFF_TIME;
585 } else {
586 chopper_config_register &= ~(RANDOM_TOFF_TIME);
587 }
588 //if started we directly send it to the motor
589 if (started) {
590 send262(chopper_config_register);
591 }
592 }
593
594 void TMC26X::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size,
595 uint8_t current_increment_step_size, uint8_t lower_current_limit)
596 {
597 //sanitize the input values
598 if (lower_SG_threshold > 480) {
599 lower_SG_threshold = 480;
600 }
601 //divide by 32
602 lower_SG_threshold >>= 5;
603 if (SG_hysteresis > 480) {
604 SG_hysteresis = 480;
605 }
606 //divide by 32
607 SG_hysteresis >>= 5;
608 if (current_decrement_step_size > 3) {
609 current_decrement_step_size = 3;
610 }
611 if (current_increment_step_size > 3) {
612 current_increment_step_size = 3;
613 }
614 if (lower_current_limit > 1) {
615 lower_current_limit = 1;
616 }
617 //store the lower level in order to enable/disable the cool step
618 this->cool_step_lower_threshold = lower_SG_threshold;
619 //if cool step is not enabled we delete the lower value to keep it disabled
620 if (!this->cool_step_enabled) {
621 lower_SG_threshold = 0;
622 }
623 //the good news is that we can start with a complete new cool step register value
624 //and simply set the values in the register
625 cool_step_register_value = ((unsigned long)lower_SG_threshold) | (((unsigned long)SG_hysteresis) << 8) | (((unsigned long)current_decrement_step_size) << 5)
626 | (((unsigned long)current_increment_step_size) << 13) | (((unsigned long)lower_current_limit) << 15)
627 //and of course we have to include the signature of the register
628 | COOL_STEP_REGISTER;
629
630 if (started) {
631 send262(cool_step_register_value);
632 }
633 }
634
635 void TMC26X::setCoolStepEnabled(bool enabled)
636 {
637 //simply delete the lower limit to disable the cool step
638 cool_step_register_value &= ~SE_MIN_PATTERN;
639 //and set it to the proper value if cool step is to be enabled
640 if (enabled) {
641 cool_step_register_value |= this->cool_step_lower_threshold;
642 }
643 //and save the enabled status
644 this->cool_step_enabled = enabled;
645 //save the register value
646 if (started) {
647 send262(cool_step_register_value);
648 }
649 }
650
651 bool TMC26X::isCoolStepEnabled(void)
652 {
653 return this->cool_step_enabled;
654 }
655
656 unsigned int TMC26X::getCoolStepLowerSgThreshold()
657 {
658 //we return our internally stored value - in order to provide the correct setting even if cool step is not enabled
659 return this->cool_step_lower_threshold << 5;
660 }
661
662 unsigned int TMC26X::getCoolStepUpperSgThreshold()
663 {
664 return (uint8_t)((cool_step_register_value & SE_MAX_PATTERN) >> 8) << 5;
665 }
666
667 uint8_t TMC26X::getCoolStepCurrentIncrementSize()
668 {
669 return (uint8_t)((cool_step_register_value & CURRENT_DOWN_STEP_SPEED_PATTERN) >> 13);
670 }
671
672 uint8_t TMC26X::getCoolStepNumberOfSGReadings()
673 {
674 return (uint8_t)((cool_step_register_value & SE_CURRENT_STEP_WIDTH_PATTERN) >> 5);
675 }
676
677 uint8_t TMC26X::getCoolStepLowerCurrentLimit()
678 {
679 return (uint8_t)((cool_step_register_value & MINIMUM_CURRENT_FOURTH) >> 15);
680 }
681
682 void TMC26X::setEnabled(bool enabled)
683 {
684 //delete the t_off in the chopper config to get sure
685 chopper_config_register &= ~(T_OFF_PATTERN);
686 if (enabled) {
687 //and set the t_off time
688 chopper_config_register |= this->constant_off_time;
689 }
690 //if not enabled we don't have to do anything since we already delete t_off from the register
691 if (started) {
692 send262(chopper_config_register);
693 }
694 }
695
696 bool TMC26X::isEnabled()
697 {
698 if (chopper_config_register & T_OFF_PATTERN) {
699 return true;
700 } else {
701 return false;
702 }
703 }
704
705 /*
706 * reads a value from the TMC26X status register. The value is not obtained directly but can then
707 * be read by the various status routines.
708 *
709 */
710 void TMC26X::readStatus(int8_t read_value)
711 {
712 unsigned long old_driver_configuration_register_value = driver_configuration_register_value;
713 //reset the readout configuration
714 driver_configuration_register_value &= ~(READ_SELECTION_PATTERN);
715 //this now equals TMC26X_READOUT_POSITION - so we just have to check the other two options
716 if (read_value == TMC26X_READOUT_STALLGUARD) {
717 driver_configuration_register_value |= READ_STALL_GUARD_READING;
718 } else if (read_value == TMC26X_READOUT_CURRENT) {
719 driver_configuration_register_value |= READ_STALL_GUARD_AND_COOL_STEP;
720 }
721 //all other cases are ignored to prevent funny values
722 //check if the readout is configured for the value we are interested in
723 if (driver_configuration_register_value != old_driver_configuration_register_value) {
724 //because then we need to write the value twice - one time for configuring, second time to get the value, see below
725 send262(driver_configuration_register_value);
726 }
727 //write the configuration to get the last status
728 send262(driver_configuration_register_value);
729 }
730
731 //reads the stall guard setting from last status
732 //returns -1 if stallguard information is not present
733 int TMC26X::getCurrentStallGuardReading(void)
734 {
735 //if we don't yet started there cannot be a stall guard value
736 if (!started) {
737 return -1;
738 }
739 //not time optimal, but solution optiomal:
740 //first read out the stall guard value
741 readStatus(TMC26X_READOUT_STALLGUARD);
742 return getReadoutValue();
743 }
744
745 uint8_t TMC26X::getCurrentCSReading(void)
746 {
747 //if we don't yet started there cannot be a stall guard value
748 if (!started) {
749 return 0;
750 }
751
752 //first read out the stall guard value
753 readStatus(TMC26X_READOUT_CURRENT);
754 return (getReadoutValue() & 0x1f);
755 }
756
757 unsigned int TMC26X::getCoolstepCurrent(void)
758 {
759 float result = (float)getCurrentCSReading();
760 float resistor_value = (float)this->resistor;
761 float voltage = (driver_configuration_register_value & VSENSE) ? 0.165F : 0.31F;
762 result = (result + 1.0F) / 32.0F * voltage / resistor_value * 1000.0F * 1000.0F;
763 return (unsigned int)roundf(result);
764 }
765
766 /*
767 return true if the stallguard threshold has been reached
768 */
769 bool TMC26X::isStallGuardOverThreshold(void)
770 {
771 if (!this->started) {
772 return false;
773 }
774 return (driver_status_result & STATUS_STALL_GUARD_STATUS);
775 }
776
777 /*
778 returns if there is any over temperature condition:
779 OVER_TEMPERATURE_PREWARING if pre warning level has been reached
780 OVER_TEMPERATURE_SHUTDOWN if the temperature is so hot that the driver is shut down
781 Any of those levels are not too good.
782 */
783 int8_t TMC26X::getOverTemperature(void)
784 {
785 if (!this->started) {
786 return 0;
787 }
788 if (driver_status_result & STATUS_OVER_TEMPERATURE_SHUTDOWN) {
789 return TMC26X_OVERTEMPERATURE_SHUTDOWN;
790 }
791 if (driver_status_result & STATUS_OVER_TEMPERATURE_WARNING) {
792 return TMC26X_OVERTEMPERATURE_PREWARING;
793 }
794 return 0;
795 }
796
797 //is motor channel A shorted to ground
798 bool TMC26X::isShortToGroundA(void)
799 {
800 if (!this->started) {
801 return false;
802 }
803 return (driver_status_result & STATUS_SHORT_TO_GROUND_A);
804 }
805
806 //is motor channel B shorted to ground
807 bool TMC26X::isShortToGroundB(void)
808 {
809 if (!this->started) {
810 return false;
811 }
812 return (driver_status_result & STATUS_SHORT_TO_GROUND_B);
813 }
814
815 //is motor channel A connected
816 bool TMC26X::isOpenLoadA(void)
817 {
818 if (!this->started) {
819 return false;
820 }
821 return (driver_status_result & STATUS_OPEN_LOAD_A);
822 }
823
824 //is motor channel B connected
825 bool TMC26X::isOpenLoadB(void)
826 {
827 if (!this->started) {
828 return false;
829 }
830 return (driver_status_result & STATUS_OPEN_LOAD_B);
831 }
832
833 //is chopper inactive since 2^20 clock cycles - defaults to ~0,08s
834 bool TMC26X::isStandStill(void)
835 {
836 if (!this->started) {
837 return false;
838 }
839 return (driver_status_result & STATUS_STAND_STILL);
840 }
841
842 //is chopper inactive since 2^20 clock cycles - defaults to ~0,08s
843 bool TMC26X::isStallGuardReached(void)
844 {
845 if (!this->started) {
846 return false;
847 }
848 return (driver_status_result & STATUS_STALL_GUARD_STATUS);
849 }
850
851 //reads the stall guard setting from last status
852 //returns -1 if stallguard inforamtion is not present
853 int TMC26X::getReadoutValue(void)
854 {
855 return (int)(driver_status_result >> 10);
856 }
857
858 bool TMC26X::isCurrentScalingHalfed()
859 {
860 if (this->driver_configuration_register_value & VSENSE) {
861 return true;
862 } else {
863 return false;
864 }
865 }
866
867 void TMC26X::dumpStatus(StreamOutput *stream, bool readable)
868 {
869 if (readable) {
870 stream->printf("designator %c, Chip type TMC26X\n", designator);
871
872 check_error_status_bits(stream);
873
874 if (this->isStallGuardReached()) {
875 stream->printf("INFO: Stall Guard level reached!\n");
876 }
877
878 if (this->isStandStill()) {
879 stream->printf("INFO: Motor is standing still.\n");
880 }
881
882 int value = getReadoutValue();
883 stream->printf("Microstep position phase A: %d\n", value);
884
885 value = getCurrentStallGuardReading();
886 stream->printf("Stall Guard value: %d\n", value);
887
888 stream->printf("Current setting: %dmA\n", getCurrent());
889 stream->printf("Coolstep current: %dmA\n", getCoolstepCurrent());
890
891 stream->printf("Microsteps: 1/%d\n", microsteps);
892
893 stream->printf("Register dump:\n");
894 stream->printf(" driver control register: %08lX(%ld)\n", driver_control_register_value, driver_control_register_value);
895 stream->printf(" chopper config register: %08lX(%ld)\n", chopper_config_register, chopper_config_register);
896 stream->printf(" cool step register: %08lX(%ld)\n", cool_step_register_value, cool_step_register_value);
897 stream->printf(" stall guard2 current register: %08lX(%ld)\n", stall_guard2_current_register_value, stall_guard2_current_register_value);
898 stream->printf(" driver configuration register: %08lX(%ld)\n", driver_configuration_register_value, driver_configuration_register_value);
899 stream->printf(" motor_driver_control.xxx.reg %05lX,%05lX,%05lX,%05lX,%05lX\n", driver_control_register_value, chopper_config_register, cool_step_register_value, stall_guard2_current_register_value, driver_configuration_register_value);
900
901 } else {
902 // TODO hardcoded for X need to select ABC as needed
903 bool moving = THEROBOT->actuators[0]->is_moving();
904 // dump out in the format that the processing script needs
905 if (moving) {
906 stream->printf("#sg%d,p%lu,k%u,r,", getCurrentStallGuardReading(), THEROBOT->actuators[0]->get_current_step(), getCoolstepCurrent());
907 } else {
908 readStatus(TMC26X_READOUT_POSITION); // get the status bits
909 stream->printf("#s,");
910 }
911 stream->printf("d%d,", THEROBOT->actuators[0]->which_direction() ? 1 : -1);
912 stream->printf("c%u,m%d,", getCurrent(), getMicrosteps());
913 // stream->printf('S');
914 // stream->printf(tmc26XStepper.getSpeed(), DEC);
915 stream->printf("t%d,f%d,", getStallGuardThreshold(), getStallGuardFilter());
916
917 //print out the general cool step config
918 if (isCoolStepEnabled()) stream->printf("Ke+,");
919 else stream->printf("Ke-,");
920
921 stream->printf("Kl%u,Ku%u,Kn%u,Ki%u,Km%u,",
922 getCoolStepLowerSgThreshold(), getCoolStepUpperSgThreshold(), getCoolStepNumberOfSGReadings(), getCoolStepCurrentIncrementSize(), getCoolStepLowerCurrentLimit());
923
924 //detect the winding status
925 if (isOpenLoadA()) {
926 stream->printf("ao,");
927 } else if(isShortToGroundA()) {
928 stream->printf("ag,");
929 } else {
930 stream->printf("a-,");
931 }
932 //detect the winding status
933 if (isOpenLoadB()) {
934 stream->printf("bo,");
935 } else if(isShortToGroundB()) {
936 stream->printf("bg,");
937 } else {
938 stream->printf("b-,");
939 }
940
941 char temperature = getOverTemperature();
942 if (temperature == 0) {
943 stream->printf("x-,");
944 } else if (temperature == TMC26X_OVERTEMPERATURE_PREWARING) {
945 stream->printf("xw,");
946 } else {
947 stream->printf("xe,");
948 }
949
950 if (isEnabled()) {
951 stream->printf("e1,");
952 } else {
953 stream->printf("e0,");
954 }
955
956 //write out the current chopper config
957 stream->printf("Cm%d,", (chopper_config_register & CHOPPER_MODE_T_OFF_FAST_DECAY) != 0);
958 stream->printf("Co%d,Cb%d,", constant_off_time, blank_time);
959 if ((chopper_config_register & CHOPPER_MODE_T_OFF_FAST_DECAY) == 0) {
960 stream->printf("Cs%d,Ce%d,Cd%d,", h_start, h_end, h_decrement);
961 }
962 stream->printf("\n");
963 }
964 }
965
966 // check error bits and report, only report once
967 bool TMC26X::check_error_status_bits(StreamOutput *stream)
968 {
969 bool error= false;
970 readStatus(TMC26X_READOUT_POSITION); // get the status bits
971
972 if (this->getOverTemperature()&TMC26X_OVERTEMPERATURE_PREWARING) {
973 if(!error_reported.test(0)) stream->printf("%c - WARNING: Overtemperature Prewarning!\n", designator);
974 error_reported.set(0);
975 }else{
976 error_reported.reset(0);
977 }
978
979 if (this->getOverTemperature()&TMC26X_OVERTEMPERATURE_SHUTDOWN) {
980 if(!error_reported.test(1)) stream->printf("%c - ERROR: Overtemperature Shutdown!\n", designator);
981 error=true;
982 error_reported.set(1);
983 }else{
984 error_reported.reset(1);
985 }
986
987 if (this->isShortToGroundA()) {
988 if(!error_reported.test(2)) stream->printf("%c - ERROR: SHORT to ground on channel A!\n", designator);
989 error=true;
990 error_reported.set(2);
991 }else{
992 error_reported.reset(2);
993 }
994
995 if (this->isShortToGroundB()) {
996 if(!error_reported.test(3)) stream->printf("%c - ERROR: SHORT to ground on channel B!\n", designator);
997 error=true;
998 error_reported.set(3);
999 }else{
1000 error_reported.reset(3);
1001 }
1002
1003 // these seem to be triggered when moving so ignore them for now
1004 if (this->isOpenLoadA()) {
1005 if(!error_reported.test(4)) stream->printf("%c - ERROR: Channel A seems to be unconnected!\n", designator);
1006 error=true;
1007 error_reported.set(4);
1008 }else{
1009 error_reported.reset(4);
1010 }
1011
1012 if (this->isOpenLoadB()) {
1013 if(!error_reported.test(5)) stream->printf("%c - ERROR: Channel B seems to be unconnected!\n", designator);
1014 error=true;
1015 error_reported.set(5);
1016 }else{
1017 error_reported.reset(5);
1018 }
1019
1020 // if(error) {
1021 // stream->printf("%08X\n", driver_status_result);
1022 // }
1023 return error;
1024 }
1025
1026 bool TMC26X::checkAlarm()
1027 {
1028 return check_error_status_bits(THEKERNEL->streams);
1029 }
1030
1031 // sets a raw register to the value specified, for advanced settings
1032 // register 255 writes them, 0 displays what registers are mapped to what
1033 // FIXME status registers not reading back correctly, check docs
1034 bool TMC26X::setRawRegister(StreamOutput *stream, uint32_t reg, uint32_t val)
1035 {
1036 switch(reg) {
1037 case 255:
1038 send262(driver_control_register_value);
1039 send262(chopper_config_register);
1040 send262(cool_step_register_value);
1041 send262(stall_guard2_current_register_value);
1042 send262(driver_configuration_register_value);
1043 stream->printf("Registers written\n");
1044 break;
1045
1046
1047 case 1: driver_control_register_value = val; stream->printf("driver control register set to %08lX\n", val); break;
1048 case 2: chopper_config_register = val; stream->printf("chopper config register set to %08lX\n", val); break;
1049 case 3: cool_step_register_value = val; stream->printf("cool step register set to %08lX\n", val); break;
1050 case 4: stall_guard2_current_register_value = val; stream->printf("stall guard2 current register set to %08lX\n", val); break;
1051 case 5: driver_configuration_register_value = val; stream->printf("driver configuration register set to %08lX\n", val); break;
1052
1053 default:
1054 stream->printf("1: driver control register\n");
1055 stream->printf("2: chopper config register\n");
1056 stream->printf("3: cool step register\n");
1057 stream->printf("4: stall guard2 current register\n");
1058 stream->printf("5: driver configuration register\n");
1059 stream->printf("255: update all registers\n");
1060 return false;
1061 }
1062 return true;
1063 }
1064
1065 /*
1066 * send register settings to the stepper driver via SPI
1067 * returns the current status
1068 * sends 20bits, the last 20 bits of the 24bits is taken as the command
1069 */
1070 void TMC26X::send262(unsigned long datagram)
1071 {
1072 uint8_t buf[] {(uint8_t)(datagram >> 16), (uint8_t)(datagram >> 8), (uint8_t)(datagram & 0xff)};
1073 uint8_t rbuf[3];
1074
1075 //write/read the values
1076 spi(buf, 3, rbuf);
1077
1078 // construct reply
1079 unsigned long i_datagram = ((rbuf[0] << 16) | (rbuf[1] << 8) | (rbuf[2])) >> 4;
1080
1081 //store the datagram as status result
1082 driver_status_result = i_datagram;
1083
1084 //THEKERNEL->streams->printf("sent: %02X, %02X, %02X received: %02X, %02X, %02X \n", buf[0], buf[1], buf[2], rbuf[0], rbuf[1], rbuf[2]);
1085 }
1086
1087 #define HAS(X) (options.find(X) != options.end())
1088 #define GET(X) (options.at(X))
1089 bool TMC26X::set_options(const options_t& options)
1090 {
1091 bool set = false;
1092 if(HAS('O') || HAS('Q')) {
1093 // void TMC26X::setStallGuardThreshold(int8_t stall_guard_threshold, int8_t stall_guard_filter_enabled)
1094 int8_t o = HAS('O') ? GET('O') : getStallGuardThreshold();
1095 int8_t q = HAS('Q') ? GET('Q') : getStallGuardFilter();
1096 setStallGuardThreshold(o, q);
1097 set = true;
1098 }
1099
1100 if(HAS('H') && HAS('I') && HAS('J') && HAS('K') && HAS('L')) {
1101 //void TMC26X::setCoolStepConfiguration(unsigned int lower_SG_threshold, unsigned int SG_hysteresis, uint8_t current_decrement_step_size, uint8_t current_increment_step_size, uint8_t lower_current_limit)
1102 setCoolStepConfiguration(GET('H'), GET('I'), GET('J'), GET('K'), GET('L'));
1103 set = true;
1104 }
1105
1106 if(HAS('S')) {
1107 uint32_t s = GET('S');
1108 if(s == 0 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) {
1109 //void TMC26X::setConstantOffTimeChopper(int8_t constant_off_time, int8_t blank_time, int8_t fast_decay_time_setting, int8_t sine_wave_offset, uint8_t use_current_comparator)
1110 setConstantOffTimeChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y'));
1111 set = true;
1112
1113 } else if(s == 1 && HAS('U') && HAS('V') && HAS('W') && HAS('X') && HAS('Y')) {
1114 //void TMC26X::setSpreadCycleChopper(int8_t constant_off_time, int8_t blank_time, int8_t hysteresis_start, int8_t hysteresis_end, int8_t hysteresis_decrement);
1115 setSpreadCycleChopper(GET('U'), GET('V'), GET('W'), GET('X'), GET('Y'));
1116 set = true;
1117
1118 } else if(s == 2 && HAS('Z')) {
1119 setRandomOffTime(GET('Z'));
1120 set = true;
1121
1122 } else if(s == 3 && HAS('Z')) {
1123 setDoubleEdge(GET('Z'));
1124 set = true;
1125
1126 } else if(s == 4 && HAS('Z')) {
1127 setStepInterpolation(GET('Z'));
1128 set = true;
1129
1130 } else if(s == 5 && HAS('Z')) {
1131 setCoolStepEnabled(GET('Z') == 1);
1132 set = true;
1133 }
1134 }
1135
1136 return set;
1137 }