Commit | Line | Data |
---|---|---|
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 | */ | |
152 | TMC26X::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 | ||
160 | void 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 | */ | |
170 | void 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 | ||
203 | void 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 | ||
241 | unsigned 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 | ||
252 | void 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 | ||
275 | int8_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 | ||
289 | int8_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 | */ | |
303 | void 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 | */ | |
351 | int TMC26X::getMicrosteps(void) | |
352 | { | |
353 | return microsteps; | |
354 | } | |
355 | ||
33483c6f JM |
356 | void 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 | ||
369 | void 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 | */ | |
405 | 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) | |
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 | ||
485 | void 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 | */ | |
558 | void 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 | ||
571 | void 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 | ||
612 | void 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 | ||
628 | bool TMC26X::isCoolStepEnabled(void) | |
629 | { | |
630 | return this->cool_step_enabled; | |
631 | } | |
632 | ||
633 | unsigned 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 | ||
639 | unsigned int TMC26X::getCoolStepUpperSgThreshold() | |
640 | { | |
641 | return (uint8_t)((cool_step_register_value & SE_MAX_PATTERN) >> 8) << 5; | |
642 | } | |
643 | ||
644 | uint8_t TMC26X::getCoolStepCurrentIncrementSize() | |
645 | { | |
646 | return (uint8_t)((cool_step_register_value & CURRENT_DOWN_STEP_SPEED_PATTERN) >> 13); | |
647 | } | |
648 | ||
649 | uint8_t TMC26X::getCoolStepNumberOfSGReadings() | |
650 | { | |
651 | return (uint8_t)((cool_step_register_value & SE_CURRENT_STEP_WIDTH_PATTERN) >> 5); | |
652 | } | |
653 | ||
654 | uint8_t TMC26X::getCoolStepLowerCurrentLimit() | |
655 | { | |
656 | return (uint8_t)((cool_step_register_value & MINIMUM_CURRENT_FOURTH) >> 15); | |
657 | } | |
658 | ||
659 | void 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 | ||
673 | bool 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 | */ | |
687 | void 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 | |
710 | int 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 | ||
722 | uint8_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 | 734 | unsigned 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 | */ | |
746 | bool 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 | */ | |
760 | int8_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 | |
775 | bool 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 | |
784 | bool 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 | |
793 | bool 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 | |
802 | bool 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 | |
811 | bool 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 | |
820 | bool 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 | |
830 | int TMC26X::getReadoutValue(void) | |
831 | { | |
832 | return (int)(driver_status_result >> 10); | |
833 | } | |
834 | ||
835 | int TMC26X::getResistor() | |
836 | { | |
837 | return this->resistor; | |
838 | } | |
839 | ||
840 | bool TMC26X::isCurrentScalingHalfed() | |
841 | { | |
842 | if (this->driver_configuration_register_value & VSENSE) { | |
843 | return true; | |
844 | } else { | |
845 | return false; | |
846 | } | |
847 | } | |
45ef09fd JM |
848 | |
849 | void 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 |
909 | bool 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 | */ |
945 | void 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 |
964 | bool 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 | } |