Commit | Line | Data |
---|---|---|
3eca6882 JM |
1 | #include "MotorDriverControl.h" |
2 | #include "libs/Kernel.h" | |
3 | #include "libs/nuts_bolts.h" | |
4 | #include "libs/utils.h" | |
5 | #include "ConfigValue.h" | |
6 | #include "libs/StreamOutput.h" | |
5b0cf319 | 7 | #include "libs/StreamOutputPool.h" |
fc320ac5 JM |
8 | #include "Robot.h" |
9 | #include "StepperMotor.h" | |
7b35a4c8 | 10 | #include "PublicDataRequest.h" |
45ef09fd | 11 | |
3eca6882 JM |
12 | #include "Gcode.h" |
13 | #include "Config.h" | |
14 | #include "checksumm.h" | |
3eca6882 | 15 | |
5b0cf319 | 16 | #include "mbed.h" // for SPI |
3eca6882 | 17 | |
45ef09fd | 18 | #include "drivers/TMC26X/TMC26X.h" |
e21e1f3a | 19 | #include "drivers/DRV8711/drv8711.h" |
45ef09fd | 20 | |
3eca6882 | 21 | #include <string> |
3eca6882 | 22 | |
5b0cf319 JM |
23 | #define motor_driver_control_checksum CHECKSUM("motor_driver_control") |
24 | #define enable_checksum CHECKSUM("enable") | |
25 | #define chip_checksum CHECKSUM("chip") | |
fc320ac5 | 26 | #define designator_checksum CHECKSUM("designator") |
7680dbb9 | 27 | #define axis_checksum CHECKSUM("axis") |
5262f8d0 JM |
28 | #define alarm_checksum CHECKSUM("alarm") |
29 | #define halt_on_alarm_checksum CHECKSUM("halt_on_alarm") | |
5b0cf319 JM |
30 | |
31 | #define current_checksum CHECKSUM("current") | |
32 | #define max_current_checksum CHECKSUM("max_current") | |
5b0cf319 | 33 | |
45ef09fd | 34 | #define microsteps_checksum CHECKSUM("microsteps") |
5b0cf319 | 35 | #define decay_mode_checksum CHECKSUM("decay_mode") |
3eca6882 | 36 | |
7b35a4c8 | 37 | #define raw_register_checksum CHECKSUM("reg") |
3eca6882 | 38 | |
45ef09fd JM |
39 | #define spi_channel_checksum CHECKSUM("spi_channel") |
40 | #define spi_cs_pin_checksum CHECKSUM("spi_cs_pin") | |
41 | #define spi_frequency_checksum CHECKSUM("spi_frequency") | |
3eca6882 | 42 | |
fc320ac5 | 43 | MotorDriverControl::MotorDriverControl(uint8_t id) : id(id) |
3eca6882 | 44 | { |
0bfaf040 | 45 | enable_event= false; |
b01faaff JM |
46 | current_override= false; |
47 | microstep_override= false; | |
3eca6882 JM |
48 | } |
49 | ||
50 | MotorDriverControl::~MotorDriverControl() | |
51 | { | |
3eca6882 JM |
52 | } |
53 | ||
54 | // this will load all motor driver controls defined in config, called from main | |
55 | void MotorDriverControl::on_module_loaded() | |
56 | { | |
57 | vector<uint16_t> modules; | |
58 | THEKERNEL->config->get_module_list( &modules, motor_driver_control_checksum ); | |
fc320ac5 | 59 | uint8_t cnt = 1; |
3eca6882 | 60 | for( auto cs : modules ) { |
5b0cf319 | 61 | // If module is enabled create an instance and initialize it |
3eca6882 | 62 | if( THEKERNEL->config->value(motor_driver_control_checksum, cs, enable_checksum )->as_bool() ) { |
fc320ac5 JM |
63 | MotorDriverControl *controller = new MotorDriverControl(cnt++); |
64 | if(!controller->config_module(cs)) delete controller; | |
3eca6882 JM |
65 | } |
66 | } | |
67 | ||
68 | // we don't need this instance anymore | |
69 | delete this; | |
70 | } | |
71 | ||
fc320ac5 | 72 | bool MotorDriverControl::config_module(uint16_t cs) |
3eca6882 | 73 | { |
7680dbb9 | 74 | std::string str= THEKERNEL->config->value( motor_driver_control_checksum, cs, axis_checksum)->by_default("")->as_string(); |
5b0cf319 | 75 | if(str.empty()) { |
7680dbb9 JM |
76 | // NOTE DEprecated use of designator for backward compatibility |
77 | str= THEKERNEL->config->value( motor_driver_control_checksum, cs, designator_checksum)->by_default("")->as_string(); | |
78 | if(str.empty()) { | |
79 | THEKERNEL->streams->printf("MotorDriverControl ERROR: axis not defined\n"); | |
80 | return false; // axis/axis required | |
81 | } | |
82 | } | |
83 | axis= str[0]; | |
84 | if( !((axis >= 'X' && axis <= 'Z') || (axis >= 'A' && axis <= 'C')) ) { | |
85 | THEKERNEL->streams->printf("MotorDriverControl ERROR: axis must be one of XYZABC\n"); | |
86 | return false; // axis is illegal | |
5b0cf319 | 87 | } |
3eca6882 | 88 | |
63b7731d JM |
89 | spi_cs_pin.from_string(THEKERNEL->config->value( motor_driver_control_checksum, cs, spi_cs_pin_checksum)->by_default("nc")->as_string())->as_output(); |
90 | if(!spi_cs_pin.connected()) { | |
7680dbb9 | 91 | THEKERNEL->streams->printf("MotorDriverControl %c ERROR: chip select not defined\n", axis); |
63b7731d JM |
92 | return false; // if not defined then we can't use this instance |
93 | } | |
94 | spi_cs_pin.set(1); | |
95 | ||
96 | ||
fc320ac5 | 97 | str= THEKERNEL->config->value( motor_driver_control_checksum, cs, chip_checksum)->by_default("")->as_string(); |
5b0cf319 | 98 | if(str.empty()) { |
7680dbb9 | 99 | THEKERNEL->streams->printf("MotorDriverControl %c ERROR: chip type not defined\n", axis); |
5b0cf319 JM |
100 | return false; // chip type required |
101 | } | |
102 | ||
45ef09fd JM |
103 | using std::placeholders::_1; |
104 | using std::placeholders::_2; | |
105 | using std::placeholders::_3; | |
106 | ||
5b0cf319 JM |
107 | if(str == "DRV8711") { |
108 | chip= DRV8711; | |
7680dbb9 | 109 | drv8711= new DRV8711DRV(std::bind( &MotorDriverControl::sendSPI, this, _1, _2, _3), axis); |
45ef09fd | 110 | |
5b0cf319 JM |
111 | }else if(str == "TMC2660") { |
112 | chip= TMC2660; | |
7680dbb9 | 113 | tmc26x= new TMC26X(std::bind( &MotorDriverControl::sendSPI, this, _1, _2, _3), axis); |
45ef09fd | 114 | |
5b0cf319 | 115 | }else{ |
7680dbb9 | 116 | THEKERNEL->streams->printf("MotorDriverControl %c ERROR: Unknown chip type: %s\n", axis, str.c_str()); |
5b0cf319 JM |
117 | return false; |
118 | } | |
119 | ||
3eca6882 | 120 | // select which SPI channel to use |
5b0cf319 | 121 | int spi_channel = THEKERNEL->config->value(motor_driver_control_checksum, cs, spi_channel_checksum)->by_default(1)->as_number(); |
3eca6882 JM |
122 | int spi_frequency = THEKERNEL->config->value(motor_driver_control_checksum, cs, spi_frequency_checksum)->by_default(1000000)->as_number(); |
123 | ||
45ef09fd | 124 | // select SPI channel to use |
3eca6882 JM |
125 | PinName mosi, miso, sclk; |
126 | if(spi_channel == 0) { | |
127 | mosi = P0_18; miso = P0_17; sclk = P0_15; | |
128 | } else if(spi_channel == 1) { | |
129 | mosi = P0_9; miso = P0_8; sclk = P0_7; | |
130 | } else { | |
7680dbb9 | 131 | THEKERNEL->streams->printf("MotorDriverControl %c ERROR: Unknown SPI Channel: %d\n", axis, spi_channel); |
5b0cf319 | 132 | return false; |
3eca6882 JM |
133 | } |
134 | ||
135 | this->spi = new mbed::SPI(mosi, miso, sclk); | |
136 | this->spi->frequency(spi_frequency); | |
fc320ac5 | 137 | this->spi->format(8, 3); // 8bit, mode3 |
3eca6882 | 138 | |
c611d712 JM |
139 | // set default max currents for each chip, can be overidden in config |
140 | switch(chip) { | |
141 | case DRV8711: max_current= 4000; break; | |
142 | case TMC2660: max_current= 4000; break; | |
143 | } | |
144 | ||
145 | max_current= THEKERNEL->config->value(motor_driver_control_checksum, cs, max_current_checksum )->by_default((int)max_current)->as_number(); // in mA | |
7b0f0de5 | 146 | //current_factor= THEKERNEL->config->value(motor_driver_control_checksum, cs, current_factor_checksum )->by_default(1.0F)->as_number(); |
5b0cf319 | 147 | |
7b0f0de5 JM |
148 | current= THEKERNEL->config->value(motor_driver_control_checksum, cs, current_checksum )->by_default(1000)->as_number(); // in mA |
149 | microsteps= THEKERNEL->config->value(motor_driver_control_checksum, cs, microsteps_checksum )->by_default(16)->as_number(); // 1/n | |
df16e9b0 | 150 | //decay_mode= THEKERNEL->config->value(motor_driver_control_checksum, cs, decay_mode_checksum )->by_default(1)->as_number(); |
5b0cf319 | 151 | |
45ef09fd | 152 | // setup the chip via SPI |
7daea248 | 153 | initialize_chip(cs); |
3eca6882 | 154 | |
7b35a4c8 JM |
155 | // if raw registers are defined set them 1,2,3 etc in hex |
156 | str= THEKERNEL->config->value( motor_driver_control_checksum, cs, raw_register_checksum)->by_default("")->as_string(); | |
157 | if(!str.empty()) { | |
158 | rawreg= true; | |
159 | std::vector<uint32_t> regs= parse_number_list(str.c_str(), 16); | |
30faab4a JM |
160 | if(!regs.empty()) { |
161 | uint32_t reg= 0; | |
162 | for(auto i : regs) { | |
163 | // this just sets the local storage, it does not write to the chip | |
164 | switch(chip) { | |
165 | case DRV8711: drv8711->set_raw_register(&StreamOutput::NullStream, ++reg, i); break; | |
166 | case TMC2660: tmc26x->setRawRegister(&StreamOutput::NullStream, ++reg, i); break; | |
167 | } | |
168 | } | |
169 | ||
170 | // write the stored registers | |
7b35a4c8 | 171 | switch(chip) { |
30faab4a JM |
172 | case DRV8711: drv8711->set_raw_register(&StreamOutput::NullStream, 255, 0); break; |
173 | case TMC2660: tmc26x->setRawRegister(&StreamOutput::NullStream, 255, 0); break; | |
7b35a4c8 JM |
174 | } |
175 | } | |
30faab4a | 176 | |
7b35a4c8 JM |
177 | }else{ |
178 | rawreg= false; | |
179 | } | |
180 | ||
3eca6882 | 181 | this->register_for_event(ON_GCODE_RECEIVED); |
7b35a4c8 | 182 | this->register_for_event(ON_HALT); |
0bfaf040 JM |
183 | this->register_for_event(ON_ENABLE); |
184 | this->register_for_event(ON_IDLE); | |
fc320ac5 | 185 | |
5262f8d0 JM |
186 | if( THEKERNEL->config->value(motor_driver_control_checksum, cs, alarm_checksum )->by_default(false)->as_bool() ) { |
187 | halt_on_alarm= THEKERNEL->config->value(motor_driver_control_checksum, cs, halt_on_alarm_checksum )->by_default(false)->as_bool(); | |
188 | // enable alarm monitoring for the chip | |
189 | this->register_for_event(ON_SECOND_TICK); | |
190 | } | |
191 | ||
7680dbb9 | 192 | THEKERNEL->streams->printf("MotorDriverControl INFO: configured motor %c (%d): as %s, cs: %04X\n", axis, id, chip==TMC2660?"TMC2660":chip==DRV8711?"DRV8711":"UNKNOWN", (spi_cs_pin.port_number<<8)|spi_cs_pin.pin); |
fc320ac5 | 193 | |
3eca6882 JM |
194 | return true; |
195 | } | |
196 | ||
0bfaf040 JM |
197 | // event to handle enable on/off, as it could be called in an ISR we schedule to turn the steppers on or off in ON_IDLE |
198 | // This may cause the initial step to be missed if on-idle is delayed too much but we can't do SPI in an interrupt | |
199 | void MotorDriverControl::on_enable(void *argument) | |
200 | { | |
d01fbc6f JM |
201 | // argument is a uin32_t where bit0 is on or off, and bit 1:X, 2:Y, 3:Z, 4:A, 5:B, 6:C etc |
202 | // for now if bit0 is 1 we turn all on, if 0 we turn all off otherwise we turn selected axis off | |
7680dbb9 | 203 | uint32_t i= (axis >= 'X' && axis <= 'Z') ? axis-'X' : axis-'A'+3; |
d01fbc6f JM |
204 | uint32_t bm= (uint32_t)argument; |
205 | if(bm == 0x01) { | |
206 | enable_event= true; | |
207 | enable_flg= true; | |
208 | ||
209 | }else if(bm == 0 || ( (bm&0x01) == 0 && (bm&(0x02<<i)) != 0 )) { | |
210 | enable_event= true; | |
211 | enable_flg= false; | |
212 | } | |
0bfaf040 JM |
213 | } |
214 | ||
215 | void MotorDriverControl::on_idle(void *argument) | |
216 | { | |
217 | if(enable_event) { | |
218 | enable_event= false; | |
219 | enable(enable_flg); | |
220 | } | |
221 | } | |
222 | ||
7b35a4c8 | 223 | void MotorDriverControl::on_halt(void *argument) |
45ef09fd | 224 | { |
7b35a4c8 JM |
225 | if(argument == nullptr) { |
226 | enable(false); | |
45ef09fd JM |
227 | } |
228 | } | |
229 | ||
5ca112ee | 230 | // runs in on_idle, does SPI transaction |
5262f8d0 JM |
231 | void MotorDriverControl::on_second_tick(void *argument) |
232 | { | |
d74b7399 JM |
233 | // we don't want to keep checking once we have been halted by an error |
234 | if(THEKERNEL->is_halted()) return; | |
235 | ||
5262f8d0 JM |
236 | bool alarm=false;; |
237 | switch(chip) { | |
238 | case DRV8711: | |
239 | alarm= drv8711->check_alarm(); | |
240 | break; | |
241 | ||
242 | case TMC2660: | |
243 | alarm= tmc26x->checkAlarm(); | |
244 | break; | |
245 | } | |
246 | ||
247 | if(halt_on_alarm && alarm) { | |
248 | THEKERNEL->call_event(ON_HALT, nullptr); | |
249 | THEKERNEL->streams->printf("Motor Driver alarm - reset or M999 required to continue\r\n"); | |
250 | } | |
251 | } | |
252 | ||
3eca6882 JM |
253 | void MotorDriverControl::on_gcode_received(void *argument) |
254 | { | |
255 | Gcode *gcode = static_cast<Gcode*>(argument); | |
256 | ||
257 | if (gcode->has_m) { | |
45ef09fd | 258 | if(gcode->m == 906) { |
7680dbb9 | 259 | if (gcode->has_letter(axis)) { |
45ef09fd | 260 | // set motor currents in mA (Note not using M907 as digipots use that) |
7680dbb9 | 261 | current= gcode->get_value(axis); |
7b0f0de5 | 262 | current= std::min(current, max_current); |
3eca6882 | 263 | set_current(current); |
b01faaff | 264 | current_override= true; |
3eca6882 JM |
265 | } |
266 | ||
7680dbb9 JM |
267 | } else if(gcode->m == 909) { // M909 Xnnn set microstepping, M909.1 also change steps/mm |
268 | if (gcode->has_letter(axis)) { | |
fc320ac5 | 269 | uint32_t current_microsteps= microsteps; |
7680dbb9 | 270 | microsteps= gcode->get_value(axis); |
fc320ac5 JM |
271 | microsteps= set_microstep(microsteps); // driver may change the steps it sets to |
272 | if(gcode->subcode == 1 && current_microsteps != microsteps) { | |
273 | // also reset the steps/mm | |
7680dbb9 | 274 | uint32_t a= (axis >= 'X' && axis <= 'Z') ? axis-'X' : axis-'A'+3; |
8e30d648 | 275 | if(a < THEROBOT->get_number_registered_motors()) { |
c8bac202 JM |
276 | float s= THEROBOT->actuators[a]->get_steps_per_mm()*((float)microsteps/current_microsteps); |
277 | THEROBOT->actuators[a]->change_steps_per_mm(s); | |
7680dbb9 | 278 | gcode->stream->printf("steps/mm for %c changed to: %f\n", axis, s); |
c8bac202 | 279 | THEROBOT->check_max_actuator_speeds(); |
fc320ac5 JM |
280 | } |
281 | } | |
b01faaff | 282 | microstep_override= true; |
5b0cf319 JM |
283 | } |
284 | ||
df16e9b0 | 285 | // } else if(gcode->m == 910) { // set decay mode |
7680dbb9 JM |
286 | // if (gcode->has_letter(axis)) { |
287 | // decay_mode= gcode->get_value(axis); | |
df16e9b0 JM |
288 | // set_decay_mode(decay_mode); |
289 | // } | |
5b0cf319 | 290 | |
7b0f0de5 | 291 | } else if(gcode->m == 911) { |
df16e9b0 | 292 | // set or get raw registers |
6e4b6254 | 293 | // M911 will dump all the registers and status of all the motors |
8e30d648 JM |
294 | // M911.1 Pn (or X0) will dump the registers and status of the selected motor. R0 will request format in processing machine readable format |
295 | // M911.2 Pn (or Y0) Rxxx Vyyy sets Register xxx to value yyy for motor nnn, xxx == 255 writes the registers, xxx == 0 shows what registers are mapped to what | |
296 | // M911.3 Pn (or Z0) will set the options based on the parameters passed as below... | |
33483c6f JM |
297 | // TMC2660:- |
298 | // M911.3 Onnn Qnnn setStallGuardThreshold O=stall_guard_threshold, Q=stall_guard_filter_enabled | |
33483c6f | 299 | // M911.3 Hnnn Innn Jnnn Knnn Lnnn setCoolStepConfiguration H=lower_SG_threshold, I=SG_hysteresis, J=current_decrement_step_size, K=current_increment_step_size, L=lower_current_limit |
6e4b6254 JM |
300 | // M911.3 S0 Unnn Vnnn Wnnn Xnnn Ynnn setConstantOffTimeChopper U=constant_off_time, V=blank_time, W=fast_decay_time_setting, X=sine_wave_offset, Y=use_current_comparator |
301 | // M911.3 S1 Unnn Vnnn Wnnn Xnnn Ynnn setSpreadCycleChopper U=constant_off_time, V=blank_time, W=hysteresis_start, X=hysteresis_end, Y=hysteresis_decrement | |
302 | // M911.3 S2 Zn setRandomOffTime Z=on|off Z1 is on Z0 is off | |
303 | // M911.3 S3 Zn setDoubleEdge Z=on|off Z1 is on Z0 is off | |
304 | // M911.3 S4 Zn setStepInterpolation Z=on|off Z1 is on Z0 is off | |
b023850b | 305 | // M911.3 S5 Zn setCoolStepEnabled Z=on|off Z1 is on Z0 is off |
33483c6f | 306 | |
df16e9b0 JM |
307 | if(gcode->subcode == 0 && gcode->get_num_args() == 0) { |
308 | // M911 no args dump status for all drivers, M911.1 P0|A0 dump for specific driver | |
7680dbb9 | 309 | gcode->stream->printf("Motor %d (%c)...\n", id, axis); |
eab91f11 | 310 | dump_status(gcode->stream, true); |
7b0f0de5 | 311 | |
7680dbb9 | 312 | }else if(gcode->get_value('P') == id || gcode->has_letter(axis)) { |
df16e9b0 | 313 | if(gcode->subcode == 1) { |
8e30d648 | 314 | dump_status(gcode->stream, !gcode->has_letter('R')); |
df16e9b0 JM |
315 | |
316 | }else if(gcode->subcode == 2 && gcode->has_letter('R') && gcode->has_letter('V')) { | |
317 | set_raw_register(gcode->stream, gcode->get_value('R'), gcode->get_value('V')); | |
318 | ||
319 | }else if(gcode->subcode == 3 ) { | |
320 | set_options(gcode); | |
321 | } | |
7b0f0de5 JM |
322 | } |
323 | ||
3eca6882 | 324 | } else if(gcode->m == 500 || gcode->m == 503) { |
b01faaff | 325 | if(current_override) { |
7680dbb9 JM |
326 | gcode->stream->printf(";Motor %c id %d current mA:\n", axis, id); |
327 | gcode->stream->printf("M906 %c%lu\n", axis, current); | |
b01faaff JM |
328 | } |
329 | if(microstep_override) { | |
7680dbb9 JM |
330 | gcode->stream->printf(";Motor %c id %d microsteps:\n", axis, id); |
331 | gcode->stream->printf("M909 %c%lu\n", axis, microsteps); | |
b01faaff | 332 | } |
7680dbb9 | 333 | //gcode->stream->printf("M910 %c%d\n", axis, decay_mode); |
b01faaff | 334 | } |
3eca6882 JM |
335 | } |
336 | } | |
337 | ||
7daea248 | 338 | void MotorDriverControl::initialize_chip(uint16_t cs) |
45ef09fd JM |
339 | { |
340 | // send initialization sequence to chips | |
341 | if(chip == DRV8711) { | |
7daea248 | 342 | drv8711->init(cs); |
fbeb173d JM |
343 | set_current(current); |
344 | set_microstep(microsteps); | |
45ef09fd JM |
345 | |
346 | }else if(chip == TMC2660){ | |
7daea248 | 347 | tmc26x->init(cs); |
45ef09fd | 348 | set_current(current); |
e21e1f3a | 349 | set_microstep(microsteps); |
fbeb173d | 350 | //set_decay_mode(decay_mode); |
45ef09fd JM |
351 | } |
352 | ||
45ef09fd JM |
353 | } |
354 | ||
e21e1f3a | 355 | // set current in milliamps |
45ef09fd | 356 | void MotorDriverControl::set_current(uint32_t c) |
3eca6882 | 357 | { |
5b0cf319 | 358 | switch(chip) { |
e21e1f3a | 359 | case DRV8711: |
fbeb173d | 360 | drv8711->set_current(c); |
e21e1f3a | 361 | break; |
45ef09fd JM |
362 | |
363 | case TMC2660: | |
364 | tmc26x->setCurrent(c); | |
365 | break; | |
5b0cf319 JM |
366 | } |
367 | } | |
368 | ||
45ef09fd | 369 | // set microsteps where n is the number of microsteps eg 64 for 1/64 |
fc320ac5 | 370 | uint32_t MotorDriverControl::set_microstep( uint32_t n ) |
5b0cf319 | 371 | { |
fc320ac5 | 372 | uint32_t m= n; |
5b0cf319 | 373 | switch(chip) { |
e21e1f3a | 374 | case DRV8711: |
fbeb173d | 375 | m= drv8711->set_microsteps(n); |
e21e1f3a | 376 | break; |
45ef09fd JM |
377 | |
378 | case TMC2660: | |
379 | tmc26x->setMicrosteps(n); | |
fc320ac5 | 380 | m= tmc26x->getMicrosteps(); |
45ef09fd | 381 | break; |
5b0cf319 | 382 | } |
fc320ac5 | 383 | return m; |
5b0cf319 JM |
384 | } |
385 | ||
0bfaf040 | 386 | // TODO how to handle this? SO many options |
5b0cf319 JM |
387 | void MotorDriverControl::set_decay_mode( uint8_t dm ) |
388 | { | |
389 | switch(chip) { | |
390 | case DRV8711: break; | |
391 | case TMC2660: break; | |
392 | } | |
393 | } | |
394 | ||
45ef09fd JM |
395 | void MotorDriverControl::enable(bool on) |
396 | { | |
397 | switch(chip) { | |
e21e1f3a JM |
398 | case DRV8711: |
399 | drv8711->set_enable(on); | |
400 | break; | |
401 | ||
45ef09fd JM |
402 | case TMC2660: |
403 | tmc26x->setEnabled(on); | |
404 | break; | |
405 | } | |
406 | } | |
407 | ||
eab91f11 | 408 | void MotorDriverControl::dump_status(StreamOutput *stream, bool b) |
45ef09fd JM |
409 | { |
410 | switch(chip) { | |
e21e1f3a JM |
411 | case DRV8711: |
412 | drv8711->dump_status(stream); | |
413 | break; | |
414 | ||
45ef09fd | 415 | case TMC2660: |
eab91f11 | 416 | tmc26x->dumpStatus(stream, b); |
45ef09fd JM |
417 | break; |
418 | } | |
419 | } | |
df16e9b0 | 420 | |
7b0f0de5 JM |
421 | void MotorDriverControl::set_raw_register(StreamOutput *stream, uint32_t reg, uint32_t val) |
422 | { | |
423 | bool ok= false; | |
424 | switch(chip) { | |
5262f8d0 | 425 | case DRV8711: ok= drv8711->set_raw_register(stream, reg, val); break; |
7b0f0de5 JM |
426 | case TMC2660: ok= tmc26x->setRawRegister(stream, reg, val); break; |
427 | } | |
428 | if(ok) { | |
df16e9b0 | 429 | stream->printf("register operation succeeded\n"); |
7b0f0de5 JM |
430 | }else{ |
431 | stream->printf("register operation failed\n"); | |
432 | } | |
433 | } | |
45ef09fd | 434 | |
df16e9b0 JM |
435 | void MotorDriverControl::set_options(Gcode *gcode) |
436 | { | |
437 | switch(chip) { | |
438 | case DRV8711: break; | |
439 | ||
440 | case TMC2660: { | |
441 | TMC26X::options_t options= gcode->get_args_int(); | |
442 | if(options.size() > 0) { | |
443 | if(tmc26x->set_options(options)) { | |
444 | gcode->stream->printf("options set\n"); | |
445 | }else{ | |
446 | gcode->stream->printf("failed to set any options\n"); | |
447 | } | |
448 | } | |
449 | // options.clear(); | |
450 | // if(tmc26x->get_optional(options)) { | |
451 | // // foreach optional value | |
452 | // for(auto &i : options) { | |
453 | // // print all current values of supported options | |
454 | // gcode->stream->printf("%c: %d ", i.first, i.second); | |
455 | // gcode->add_nl = true; | |
456 | // } | |
457 | // } | |
458 | } | |
459 | break; | |
460 | } | |
461 | } | |
462 | ||
0bfaf040 | 463 | // Called by the drivers codes to send and receive SPI data to/from the chip |
45ef09fd JM |
464 | int MotorDriverControl::sendSPI(uint8_t *b, int cnt, uint8_t *r) |
465 | { | |
466 | spi_cs_pin.set(0); | |
467 | for (int i = 0; i < cnt; ++i) { | |
468 | r[i]= spi->write(b[i]); | |
469 | } | |
470 | spi_cs_pin.set(1); | |
471 | return cnt; | |
472 | } | |
473 |