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