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