Open FFBoard
Open source force feedback firmware
Axis.cpp
Go to the documentation of this file.
1/*
2 * Axis.cpp
3 *
4 * Created on: 31.01.2020
5 * Author: Yannick
6 */
7
8#include "Axis.h"
9#include "voltagesense.h"
10#include "TMC4671.h"
11#include "MotorPWM.h"
12#include "VescCAN.h"
13#include "ODriveCAN.h"
14#include "MotorSimplemotion.h"
15#include "RmdMotorCAN.h"
16
18/*
19 * Sources for class choosers are defined in MotorDriver and Encoder
20 */
21
22
24
26 .name = "Axis",
27 .id = CLSID_AXIS, // 1
28 .visibility = ClassVisibility::visible};
29
33const std::vector<class_entry<MotorDriver>> Axis::axis1_drivers =
34{
35 add_class<MotorDriver, MotorDriver>(0),
36
37#ifdef TMC4671DRIVER
38 add_class<TMC_1, MotorDriver>(1),
39#endif
40#ifdef PWMDRIVER
41 add_class<MotorPWM, MotorDriver>(4),
42#endif
43#ifdef ODRIVE
44 add_class<ODriveCAN1,MotorDriver>(5),
45#endif
46#ifdef VESC
47 add_class<VESC_1,MotorDriver>(7),
48#endif
49#ifdef SIMPLEMOTION
50 add_class<MotorSimplemotion1,MotorDriver>(9),
51#endif
52#ifdef RMDCAN
53 add_class<RmdMotorCAN1,MotorDriver>(11),
54#endif
55};
56
60const std::vector<class_entry<MotorDriver>> Axis::axis2_drivers =
61{
62 add_class<MotorDriver, MotorDriver>(0),
63
64#ifdef TMC4671DRIVER
65 add_class<TMC_2, MotorDriver>(2),
66#endif
67#ifdef PWMDRIVER
68 add_class<MotorPWM, MotorDriver>(4),
69#endif
70#ifdef ODRIVE
71 add_class<ODriveCAN2,MotorDriver>(6),
72#endif
73#ifdef VESC
74 add_class<VESC_2,MotorDriver>(8),
75#endif
76#ifdef RMDCAN
77 add_class<RmdMotorCAN2,MotorDriver>(12),
78#endif
79//#ifdef SIMPLEMOTION
80// add_class<MotorSimplemotion2,MotorDriver>(10), // TODO this likely does not work reliably with a single uart port and multiple devices
81//#endif
82};
83
84
88Axis::Axis(char axis,volatile Control_t* control) :CommandHandler("axis", CLSID_AXIS), drv_chooser(MotorDriver::all_drivers),enc_chooser{Encoder::all_encoders}
89{
90 this->axis = axis;
91 this->control = control;
92 if (axis == 'X')
93 {
95 setInstance(0);
96 this->flashAddrs = AxisFlashAddrs({ADR_AXIS1_CONFIG, ADR_AXIS1_MAX_SPEED, ADR_AXIS1_MAX_ACCEL,
97 ADR_AXIS1_ENDSTOP, ADR_AXIS1_POWER, ADR_AXIS1_DEGREES,ADR_AXIS1_EFFECTS1,ADR_AXIS1_EFFECTS2,ADR_AXIS1_ENC_RATIO,
98 ADR_AXIS1_SPEEDACCEL_FILTER});
99 }
100 else if (axis == 'Y')
101 {
103 setInstance(1);
104 this->flashAddrs = AxisFlashAddrs({ADR_AXIS2_CONFIG, ADR_AXIS2_MAX_SPEED, ADR_AXIS2_MAX_ACCEL,
105 ADR_AXIS2_ENDSTOP, ADR_AXIS2_POWER, ADR_AXIS2_DEGREES,ADR_AXIS2_EFFECTS1,ADR_AXIS2_EFFECTS2, ADR_AXIS2_ENC_RATIO,
106 ADR_AXIS2_SPEEDACCEL_FILTER});
107 }
108 else if (axis == 'Z')
109 {
110 setInstance(2);
111 this->flashAddrs = AxisFlashAddrs({ADR_AXIS3_CONFIG, ADR_AXIS3_MAX_SPEED, ADR_AXIS3_MAX_ACCEL,
112 ADR_AXIS3_ENDSTOP, ADR_AXIS3_POWER, ADR_AXIS3_DEGREES,ADR_AXIS3_EFFECTS1,ADR_AXIS3_EFFECTS2,ADR_AXIS3_ENC_RATIO,
113 ADR_AXIS3_SPEEDACCEL_FILTER});
114 }
115
116
117 restoreFlash(); // Load parameters
118 CommandHandler::registerCommands(); // Internal commands
120 updateTorqueScaler(); // In case no flash setting has been loaded yet
121}
122
124{
125
126}
127
129
130 return info;
131}
132
134 registerCommand("power", Axis_commands::power, "Overall force strength",CMDFLAG_GET | CMDFLAG_SET);
135 registerCommand("degrees", Axis_commands::degrees, "Rotation range in deg",CMDFLAG_GET | CMDFLAG_SET);
136 registerCommand("esgain", Axis_commands::esgain, "Endstop stiffness",CMDFLAG_GET | CMDFLAG_SET);
137 registerCommand("zeroenc", Axis_commands::zeroenc, "Zero axis",CMDFLAG_GET);
138 registerCommand("invert", Axis_commands::invert, "Invert axis",CMDFLAG_GET | CMDFLAG_SET);
139 registerCommand("idlespring", Axis_commands::idlespring, "Idle spring strength",CMDFLAG_GET | CMDFLAG_SET);
140 registerCommand("axisdamper", Axis_commands::axisdamper, "Independent damper effect",CMDFLAG_GET | CMDFLAG_SET);
141 registerCommand("axisfriction", Axis_commands::axisfriction, "Independent friction effect",CMDFLAG_GET | CMDFLAG_SET);
142 registerCommand("axisinertia", Axis_commands::axisinertia, "Independent inertia effect",CMDFLAG_GET | CMDFLAG_SET);
143 registerCommand("enctype", Axis_commands::enctype, "Encoder type get/set/list",CMDFLAG_GET | CMDFLAG_SET | CMDFLAG_INFOSTRING);
144 registerCommand("drvtype", Axis_commands::drvtype, "Motor driver type get/set/list",CMDFLAG_GET | CMDFLAG_SET | CMDFLAG_INFOSTRING);
145 registerCommand("pos", Axis_commands::pos, "Encoder position",CMDFLAG_GET);
146 registerCommand("maxspeed", Axis_commands::maxspeed, "Speed limit in deg/s",CMDFLAG_GET | CMDFLAG_SET);
147 registerCommand("maxtorquerate", Axis_commands::maxtorquerate, "Torque rate limit in counts/ms",CMDFLAG_GET | CMDFLAG_SET);
148 registerCommand("fxratio", Axis_commands::fxratio, "Effect ratio. Reduces game effects excluding endstop. 255=100%",CMDFLAG_GET | CMDFLAG_SET);
149 registerCommand("curtorque", Axis_commands::curtorque, "Axis torque",CMDFLAG_GET);
150 registerCommand("curpos", Axis_commands::curpos, "Axis position",CMDFLAG_GET);
151 registerCommand("curspd", Axis_commands::curspd, "Axis speed",CMDFLAG_GET);
152 registerCommand("curaccel", Axis_commands::curaccel, "Axis accel",CMDFLAG_GET);
153 registerCommand("reduction", Axis_commands::reductionScaler, "Encoder to axis gear reduction (val / adr) 1-256",CMDFLAG_GET | CMDFLAG_SETADR);
154 registerCommand("filterProfile_id", Axis_commands::filterProfileId, "Biquad filter profile for speed and accel", CMDFLAG_GET | CMDFLAG_SET);
155 //Can only read exact filter settings
156 registerCommand("filterSpeed", Axis_commands::filterSpeed, "Biquad filter freq and q*100 for speed", CMDFLAG_GET);
157 registerCommand("filterAccel", Axis_commands::filterAccel, "Biquad filter freq and q*100 for accel", CMDFLAG_GET);
158
159 registerCommand("cpr", Axis_commands::cpr, "Reported encoder CPR",CMDFLAG_GET);
160}
161
162/*
163 * Read parameters from flash and restore settings
164 */
166 //NormalizedAxis::restoreFlash();
167 // read all constants
168 uint16_t value;
169 if (Flash_Read(flashAddrs.config, &value)){
170 this->conf = Axis::decodeConfFromInt(value);
171 }else{
172 pulseErrLed();
173 }
174
175 setDrvType(this->conf.drvtype);
176 setEncType(this->conf.enctype);
177
178 if (Flash_Read(flashAddrs.maxSpeed, &value)){
179 this->maxSpeedDegS = value;
180 }else{
181 pulseErrLed();
182 }
183//
184// if (Flash_Read(flashAddrs.maxAccel, &value)){
185// this->maxTorqueRateMS = value;
186// }else{
187// pulseErrLed();
188// }
189
190
191 uint16_t esval, power;
192 if(Flash_Read(flashAddrs.endstop, &esval)) {
193 fx_ratio_i = esval & 0xff;
194 endstopStrength = (esval >> 8) & 0xff;
195 }
196
197
200 }
201 uint16_t deg_t;
202 if(Flash_Read(flashAddrs.degrees, &deg_t)){
203 this->degreesOfRotation = deg_t & 0x7fff;
204 this->invertAxis = (deg_t >> 15) & 0x1;
206 }
207
208
209 uint16_t effects;
213 }else{
215 }
216
220 }
221
222 uint16_t ratio;
224 setGearRatio(ratio & 0xff, (ratio >> 8) & 0xff);
225 }
226
227 uint16_t filterStorage;
228 if (Flash_Read(flashAddrs.speedAccelFilter, &filterStorage))
229 {
230 uint8_t profile = filterStorage & 0xFF;
231 this->filterProfileId = profile;
236 }
237
238}
239// Saves parameters to flash.
241 //NormalizedAxis::saveFlash();
243 Flash_Write(flashAddrs.maxSpeed, this->maxSpeedDegS);
244// Flash_Write(flashAddrs.maxAccel, (uint16_t)(this->maxTorqueRateMS));
245
252
253 // save CF biquad
254 uint16_t filterStorage = (uint16_t)this->filterProfileId & 0xFF;
256}
257
258
260 return (uint8_t)this->conf.drvtype;
261}
262
264 if(drv->hasIntegratedEncoder()){
265 return 255;
266 }
267 return (uint8_t)this->conf.enctype;
268}
269
270
271void Axis::setPos(uint16_t val)
272{
273 startForceFadeIn(0.25,0.5);
274 if(this->drv != nullptr){
275 drv->getEncoder()->setPos(val);
276 }
277}
278
280 return drv.get();
281}
282
284 return drv->getEncoder();
285}
286
291 if (drv == nullptr){
292 pulseErrLed();
293 return;
294 }
295
296 //if (!drv->motorReady()) return;
297
298 float angle = getEncAngle(this->drv->getEncoder());
299
300 // Scale encoder value to set rotation range
301 // Update a change of range only when new range is within valid range
302 // if degree change, compute the SpeedScaler, it depends on degreesOfRotation
304 int32_t scaledEnc;
305 std::tie(scaledEnc,std::ignore) = scaleEncValue(angle, nextDegreesOfRotation);
306 if(abs(scaledEnc) < 0x7fff){
308 }
309
310 }
311
312
313 // scaledEnc now gets inverted if necessary in updateMetrics
314 int32_t scaledEnc;
315 std::tie(scaledEnc,std::ignore) = scaleEncValue(angle, degreesOfRotation);
316
317 if (abs(scaledEnc) > 0xffff && drv->motorReady()){
318 // We are way off. Shut down
319 drv->stopMotor();
320 pulseErrLed();
321 if(!outOfBounds){
322 outOfBounds = true;
324 }
325
326 }else if(abs(scaledEnc) <= 0x7fff) {
327 outOfBounds = false;
328 //ErrorHandler::clearError(outOfBoundsError);
329 }
330
331 // On first change to ready start a fade
332 if(motorWasNotReady && drv->motorReady()){
333 motorWasNotReady = false;
334 startForceFadeIn(0, 1.0);
335 }
336
337
338 this->updateMetrics(angle);
339
340}
341
342void Axis::errorCallback(const Error &error, bool cleared){
343 if(cleared && error == this->outOfBoundsError){
344 drv->startMotor();
345 outOfBounds = false;
346 }
347}
348
349
351 // totalTorque = effectTorque + endstopTorque
352 int32_t totalTorque;
353 bool torqueChanged = updateTorque(&totalTorque);
354 if (torqueChanged && drv->motorReady()){
355 // Send to motor driver
356 drv->turn(totalTorque);
357 }
358}
359
360void Axis::setPower(uint16_t power)
361{
362 this->power = power;
364#ifdef TMC4671DRIVER
365 // Update hardware limits for TMC for safety
366 TMC4671 *drv = dynamic_cast<TMC4671 *>(this->drv.get());
367 if (drv != nullptr)
368 {
369 //tmclimits.pid_uq_ud = power;
370 //tmclimits.pid_torque_flux = power;
371 drv->setTorqueLimit(power);
372 }
373#endif
374}
375
376
380void Axis::setDrvType(uint8_t drvtype)
381{
382 if (!drv_chooser.isValidClassId(drvtype))
383 {
384 return;
385 }
386 this->drv.reset(nullptr);
387 MotorDriver* drv = drv_chooser.Create((uint16_t)drvtype);
388 if (drv == nullptr)
389 {
390 return;
391 }
392 this->drv = std::unique_ptr<MotorDriver>(drv);
393 this->conf.drvtype = drvtype;
394
395 // Pass encoder to driver again
396 if(!this->drv->hasIntegratedEncoder()){
397 this->drv->setEncoder(this->enc);
398 }
399#ifdef TMC4671DRIVER
400 if (dynamic_cast<TMC4671 *>(drv))
401 {
402 setupTMC4671();
403 }
404#endif
405 if (!tud_connected())
406 {
407 control->usb_disabled = false;
408 this->usbSuspend();
409 }
410 else
411 {
412 drv->startMotor();
413 }
414}
415
416#ifdef TMC4671DRIVER
417// Special tmc setup methods
419{
420 TMC4671 *drv = static_cast<TMC4671 *>(this->drv.get());
421// drv->setAxis(axis);
422 drv->setExternalEncoderAllowed(true);
423 drv->restoreFlash();
425 drv->setLimits(tmclimits);
426 //drv->setBiquadTorque(TMC4671Biquad(tmcbq_500hz_07q_25k));
427
428
429 // Enable driver
430
431 drv->setMotionMode(MotionMode::torque);
432 drv->Start(); // Start thread
433}
434#endif
435
436
440void Axis::setEncType(uint8_t enctype)
441{
442 if (enc_chooser.isValidClassId(enctype) && !drv->hasIntegratedEncoder())
443 {
444
445 this->conf.enctype = (enctype);
446 this->enc = std::shared_ptr<Encoder>(enc_chooser.Create(enctype)); // Make new encoder
447 if(drv && !drv->hasIntegratedEncoder())
448 this->drv->setEncoder(this->enc);
449 }else{
450 this->conf.enctype = 0; // None encoder
451 }
452
453 float angle = getEncAngle(this->drv->getEncoder());
454 //int32_t scaledEnc = scaleEncValue(angle, degreesOfRotation);
455 // reset metrics
456 this->resetMetrics(angle);
457
458}
459
464void Axis::setGearRatio(uint8_t numerator,uint8_t denominator){
465 this->gearRatio.denominator = denominator;
466 this->gearRatio.numerator = numerator;
467 this->gearRatio.gearRatio = ((float)numerator+1.0)/((float)denominator+1.0);
468}
469
475std::pair<int32_t,float> Axis::scaleEncValue(float angle, uint16_t degrees){
476 if (degrees == 0){
477 return std::make_pair<int32_t,float>(0x7fff,0.0);
478 }
479
480 int32_t val = (0xffff / (float)degrees) * angle;
481 float val_f = (2.0 / (float)degrees) * angle;
482
483 return std::make_pair(val,val_f);
484}
485
490 if(enc != nullptr){
491 float pos = 360.0 * enc->getPos_f() * gearRatio.gearRatio;
492 if (isInverted()){
493 pos= -pos;
494 }
495 return pos;
496 }
497 else{
498 return 0;
499 }
500}
501
505void Axis::emergencyStop(bool reset){
506 drv->turn(0); // Send 0 torque first
507 if(reset){
509 }
510 drv->emergencyStop(reset);
511 control->emergency = !reset;
512}
513
518 if (drv != nullptr){
519 drv->turn(0);
520 drv->stopMotor();
521 }
522}
523
529 if (drv != nullptr){
530 drv->startMotor();
531 }
532}
533
534
535
537 return &metric.current;
538}
539
544 return clip(metric.current.pos,-0x7fff,0x7fff);
545}
546
551 return clip<int32_t,int32_t>((int32_t)(-metric.current.pos*idlespringscale),-idlespringclip,idlespringclip);
552}
553
554/*
555 * Set the strength of the spring effect if FFB is disabled
556 */
557void Axis::setIdleSpringStrength(uint8_t spring){
559 idlespringclip = clip<int32_t,int32_t>((int32_t)spring*35,0,10000);
560 idlespringscale = 0.5f + ((float)spring * 0.01f);
561}
562
566void Axis::setFxStrengthAndFilter(uint8_t val,uint8_t& valToSet, Biquad& filter){
567 if(valToSet == 0 && val != 0)
568 filter.calcBiquad();
569
570 valToSet = val;
571}
572
579
580 if(!ffb_on){
582 }
583
584 // Always active damper
585 if(damperIntensity != 0){
586 float speedFiltered = (metric.current.speed) * (float)damperIntensity * AXIS_DAMPER_RATIO;
587 axisEffectTorque -= damperFilter.process(clip<float, int32_t>(speedFiltered, -intFxClip, intFxClip));
588 }
589
590 // Always active inertia
591 if(inertiaIntensity != 0){
592 float accelFiltered = metric.current.accel * (float)inertiaIntensity * AXIS_INERTIA_RATIO;
593 axisEffectTorque -= inertiaFilter.process(clip<float, int32_t>(accelFiltered, -intFxClip, intFxClip));
594 }
595
596 // Always active friction. Based on effectsCalculator implementation
597 if(frictionIntensity != 0){
598 float speed = metric.current.speed * INTERNAL_SCALER_FRICTION;
599 float speedRampupCeil = 4096;
600 float rampupFactor = 1.0;
601 if (fabs (speed) < speedRampupCeil) { // if speed in the range to rampup we apply a sinus curbe to ramup
602 float phaseRad = M_PI * ((fabs (speed) / speedRampupCeil) - 0.5);// we start to compute the normalized angle (speed / normalizedSpeed@5%) and translate it of -1/2PI to translate sin on 1/2 periode
603 rampupFactor = ( 1 + sin(phaseRad ) ) / 2; // sin value is -1..1 range, we translate it to 0..2 and we scale it by 2
604 }
605 int8_t sign = speed >= 0 ? 1 : -1;
606 float force = (float)frictionIntensity * rampupFactor * sign * INTERNAL_AXIS_FRICTION_SCALER * 32;
607 axisEffectTorque -= frictionFilter.process(clip<float, int32_t>(force, -intFxClip, intFxClip));
608 }
609
610}
611
615void Axis::setFxRatio(uint8_t val) {
616 fx_ratio_i = val;
618}
619
623void Axis::resetMetrics(float new_pos= 0) { // pos is degrees
625 metric.current.posDegrees = new_pos;
628 // Reset filters
631}
632
636void Axis::updateMetrics(float new_pos) { // pos is degrees
637 // store old value for next metric's computing
639
640 metric.current.posDegrees = new_pos;
642
643
644 // compute speed and accel from raw instant speed normalized
645 float currentSpeed = (new_pos - metric.previous.posDegrees) * 1000.0; // deg/s
646 metric.current.speed = speedFilter.process(currentSpeed);
647 metric.current.accel = accelFilter.process((currentSpeed - _lastSpeed))* 1000.0; // deg/s/s
648 _lastSpeed = currentSpeed;
649
650}
651
652
653
654uint16_t Axis::getPower(){
655 return power;
656}
657
659 effect_margin_scaler = ((float)fx_ratio_i/255.0);
660 torqueScaler = ((float)power / (float)0x7fff);
661}
662
664 return torqueScaler;
665}
666
667
669
671 return invertAxis;
672}
673
678 int8_t clipdir = cliptest<int32_t,int32_t>(metric.current.pos, -0x7fff, 0x7fff);
679 if(clipdir == 0){
680 return 0;
681 }
682 float addtorque = clipdir*metric.current.posDegrees - (float)this->degreesOfRotation/2.0; // degress of rotation counts total range so multiply by 2
683 addtorque *= (float)endstopStrength * endstopGain; // Apply endstop gain for stiffness.
684 addtorque *= -clipdir;
685
686 return clip<int32_t,int32_t>(addtorque,-0x7fff,0x7fff);
687}
688
689void Axis::setEffectTorque(int32_t torque) {
691}
692
697bool Axis::updateTorque(int32_t* totalTorque) {
698
699 if(abs(effectTorque) >= 0x7fff){
700 pulseClipLed();
701 }
702
703 // Scale effect torque
704 int32_t torque = effectTorque; // Game effects
706 torque += axisEffectTorque; // Independent effects
708 torque *= torqueScaler; // Scale to power
709
710 // TODO speed and accel limiters
711 if(maxSpeedDegS > 0){
712
713 float torqueSign = torque > 0 ? 1 : -1; // Used to prevent metrics against the force to go into the limiter
714 // Speed. Mostly tuned...
715 //spdlimiterAvg.addValue(metric.current.speed);
716 float speedreducer = (float)((metric.current.speed*torqueSign) - (float)maxSpeedDegS) * ((float)0x7FFF / maxSpeedDegS);
717 spdlimitreducerI = clip<float,int32_t>( spdlimitreducerI + ((speedreducer * speedLimiterI) * torqueScaler),0,power);
718
719 // Accel limit. Not really useful. Maybe replace with torque slew rate limit?
720// float accreducer = (float)((metric.current.accel*torqueSign) - (float)maxAccelDegSS) * getAccelScalerNormalized();
721// acclimitreducerI = clip<float,int32_t>( acclimitreducerI + ((accreducer * 0.02) * torqueScaler),0,power);
722
723
724 // Only reduce torque. Don't invert it to prevent oscillation
725 float torqueReduction = speedreducer * speedLimiterP + spdlimitreducerI;// accreducer * 0.025 + acclimitreducerI
726 if(torque > 0){
727 torqueReduction = clip<float,int32_t>(torqueReduction,0,torque);
728 }else{
729 torqueReduction = clip<float,int32_t>(-torqueReduction,torque,0);
730 }
731
732 torque -= torqueReduction;
733 }
734 // Torque slew rate limiter
735 if(maxTorqueRateMS > 0){
737 }
738// if(torque - metric.previous.torque)
739 if(outOfBounds){
740 torque = 0;
741 }
742
743 // Fade in
744 if(forceFadeCurMult < 1){
746 forceFadeCurMult += forceFadeTime / this->filter_f; // Fade time
747 }
748
749 // Torque calculated. Now sending to driver
750 torque = (invertAxis) ? -torque : torque;
752 torque = clip<int32_t, int32_t>(torque, -power, power);
753
754 bool torqueChanged = metric.current.torque != metric.previous.torque;
755
756 if (abs(torque) == power){
757 pulseClipLed();
758 }
759
760 *totalTorque = torque;
761 return (torqueChanged);
762}
763
767void Axis::startForceFadeIn(float start,float fadeTime){
768 this->forceFadeTime = fadeTime;
769 this->forceFadeCurMult = clip<float>(start, 0, 1);
770}
771
772
776void Axis::setDegrees(uint16_t degrees){
777
778 degrees &= 0x7fff;
779 if(degrees == 0){
781 }else{
784 }
785}
786
787
788CommandStatus Axis::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
789
790 switch(static_cast<Axis_commands>(cmd.cmdId)){
791
793 if (cmd.type == CMDtype::get)
794 {
795 replies.emplace_back(this->power);
796 }
797 else if (cmd.type == CMDtype::set)
798 {
799 setPower(cmd.val);
800 }
801 break;
802
805// if (cmd.type == CMDtype::get)
806// {
807// replies.emplace_back(degreesOfRotation);
808// }
809// else if (cmd.type == CMDtype::set)
810// {
811// setDegrees(cmd.val);
812// }
813 break;
814
816 handleGetSet(cmd, replies, this->endstopStrength);
817 break;
818
820 this->setPos(0);
821 break;
822
824 if (cmd.type == CMDtype::get)
825 {
826 replies.emplace_back(invertAxis ? 1 : 0);
827 }
828 else if (cmd.type == CMDtype::set)
829 {
830 invertAxis = cmd.val >= 1 ? true : false;
832 }
833 break;
834
836 if (cmd.type == CMDtype::get)
837 {
838 replies.emplace_back(idlespringstrength);
839 }
840 else if (cmd.type == CMDtype::set)
841 {
843 }
844 break;
845
847 if (cmd.type == CMDtype::get)
848 {
849 replies.emplace_back(damperIntensity);
850 }
851 else if (cmd.type == CMDtype::set)
852 {
853 setFxStrengthAndFilter(cmd.val,this->damperIntensity,this->damperFilter);
854 }
855 break;
856
858 if (cmd.type == CMDtype::get)
859 {
860 replies.emplace_back(frictionIntensity);
861 }
862 else if (cmd.type == CMDtype::set)
863 {
864 setFxStrengthAndFilter(cmd.val,this->frictionIntensity,this->frictionFilter);
865 }
866 break;
867
869 if (cmd.type == CMDtype::get)
870 {
871 replies.emplace_back(inertiaIntensity);
872 }
873 else if (cmd.type == CMDtype::set)
874 {
875 setFxStrengthAndFilter(cmd.val,this->inertiaIntensity,this->inertiaFilter);
876 }
877 break;
878
880 if(cmd.type == CMDtype::info){
881 enc_chooser.replyAvailableClasses(replies,this->getEncType());
882 }else if(cmd.type == CMDtype::get){
883 replies.emplace_back(this->getEncType());
884 }else if(cmd.type == CMDtype::set){
885 this->setEncType(cmd.val);
886 }
887 break;
888
890 if(cmd.type == CMDtype::info){
891 drv_chooser.replyAvailableClasses(replies,this->getDrvType());
892 }else if(cmd.type == CMDtype::get){
893 replies.emplace_back(this->getDrvType());
894 }else if(cmd.type == CMDtype::set){
895 this->setDrvType(cmd.val);
896 }
897 break;
898
900 if (cmd.type == CMDtype::get && this->drv->getEncoder() != nullptr)
901 {
902 int32_t pos = this->drv->getEncoder()->getPos();
903 replies.emplace_back(isInverted() ? -pos : pos);
904 }
905 else if (cmd.type == CMDtype::set && this->drv->getEncoder() != nullptr)
906 {
907 this->drv->getEncoder()->setPos(isInverted() ? -cmd.val : cmd.val);
908 }
909 else
910 {
911 return CommandStatus::ERR;
912 }
913 break;
914
916 handleGetSet(cmd, replies, this->maxSpeedDegS);
917 break;
918
920 handleGetSet(cmd, replies, this->maxTorqueRateMS);
921 break;
922
924 if(cmd.type == CMDtype::get){
925 replies.emplace_back(this->fx_ratio_i);
926 }else if(cmd.type == CMDtype::set){
927 setFxRatio(cmd.val);
928 }
929 break;
930
932 replies.emplace_back(this->metric.current.pos);
933 break;
935 replies.emplace_back(this->metric.current.torque);
936 break;
938 replies.emplace_back(this->metric.current.speed);
939 break;
941 replies.emplace_back(this->metric.current.accel);
942 break;
943
945 if(cmd.type == CMDtype::get){
946 replies.emplace_back(gearRatio.numerator+1,gearRatio.denominator+1);
947 }else if(cmd.type == CMDtype::setat){
948 setGearRatio(cmd.val-1,cmd.adr-1);
949 }
950 break;
951
953 if (cmd.type == CMDtype::get)
954 {
955 replies.emplace_back(this->filterProfileId);
956 }
957 else if (cmd.type == CMDtype::set)
958 {
959 uint32_t value = clip<uint32_t, uint32_t>(cmd.val, 0, filterSpeedCst.size()-1);
960 this->filterProfileId = value;
965 }
966 break;
968 if (cmd.type == CMDtype::get)
969 {
970 replies.emplace_back(this->filterSpeedCst[this->filterProfileId].freq,this->filterSpeedCst[this->filterProfileId].q);
971 }
972 break;
973
975 if (cmd.type == CMDtype::get)
976 {
977 replies.emplace_back(this->filterAccelCst[this->filterProfileId].freq,this->filterAccelCst[this->filterProfileId].q);
978 }
979 break;
980
982 if (cmd.type == CMDtype::get)
983 {
984 uint32_t cpr = 0;
985 if(this->drv->getEncoder() != nullptr){
986 cpr = this->drv->getEncoder()->getCpr();
987 }
988//#ifdef TMC4671DRIVER // CPR should be consistent with position. Maybe change TMC to prescale to encoder count or correct readout in UI
989// TMC4671 *tmcdrv = dynamic_cast<TMC4671 *>(this->drv.get()); // Special case for TMC. Get the actual encoder resolution
990// if (tmcdrv && tmcdrv->hasIntegratedEncoder())
991// {
992// cpr = tmcdrv->getEncCpr();
993// }
994//#endif
995 replies.emplace_back(cpr);
996 }else{
997 return CommandStatus::ERR;
998 }
999 break;
1000
1001 default:
1003 }
1004 return CommandStatus::OK;
1005}
1006
1007
1008
1009/*
1010 * Helper functions for encoding and decoding flash variables
1011 */
1013{
1014 // 0-6 enc, 7-12 Mot
1016 conf.enctype = ((val)&0x3f);
1017 conf.drvtype = ((val >> 6) & 0x3f);
1018 return conf;
1019}
1020
1022{
1023 uint16_t val = (uint8_t)conf.enctype & 0x3f;
1024 val |= ((uint8_t)conf.drvtype & 0x3f) << 6;
1025 return val;
1026}
Axis_commands
Definition: Axis.h:92
CommandStatus
float forceFadeTime
Definition: Axis.h:195
float effect_margin_scaler
Definition: Axis.h:239
float getTorqueScaler()
Definition: Axis.cpp:663
void errorCallback(const Error &error, bool cleared) override
Definition: Axis.cpp:342
const std::array< biquad_constant_t, 4 > filterAccelCst
Definition: Axis.h:252
float getEncAngle(Encoder *enc)
Definition: Axis.cpp:489
uint8_t idlespringstrength
Definition: Axis.h:245
float speedLimiterI
Definition: Axis.h:217
AxisFlashAddrs flashAddrs
Definition: Axis.h:179
void startForceFadeIn(float start=0, float fadeTime=0.5)
Definition: Axis.cpp:767
int32_t getTorque()
Definition: Axis.cpp:668
axis_metric_t metric
Definition: Axis.h:232
void setEffectTorque(int32_t torque)
Definition: Axis.cpp:689
GearRatio_t gearRatio
Definition: Axis.h:275
bool isInverted()
Definition: Axis.cpp:670
uint16_t nextDegreesOfRotation
Definition: Axis.h:210
Axis(char axis, volatile Control_t *control)
Definition: Axis.cpp:88
uint8_t endstopStrength
Definition: Axis.h:241
float forceFadeCurMult
Definition: Axis.h:196
float torqueScaler
Definition: Axis.h:238
char axis
Definition: Axis.h:228
ClassChooser< MotorDriver > drv_chooser
Definition: Axis.h:149
uint8_t frictionIntensity
Definition: Axis.h:261
void setFxStrengthAndFilter(uint8_t val, uint8_t &valToSet, Biquad &filter)
Definition: Axis.cpp:566
int32_t updateIdleSpringForce()
Definition: Axis.cpp:550
void setDegrees(uint16_t degrees)
Definition: Axis.cpp:776
void calculateAxisEffects(bool ffb_on)
Definition: Axis.cpp:577
bool updateTorque(int32_t *totalTorque)
Definition: Axis.cpp:697
uint8_t damperIntensity
Definition: Axis.h:259
uint8_t filterProfileId
Definition: Axis.h:256
uint16_t getPower()
Definition: Axis.cpp:654
uint8_t getEncType()
Definition: Axis.cpp:263
volatile Control_t * control
Definition: Axis.h:180
std::pair< int32_t, float > scaleEncValue(float angle, uint16_t degrees)
Definition: Axis.cpp:475
void setIdleSpringStrength(uint8_t spring)
Definition: Axis.cpp:557
const float filter_f
Definition: Axis.h:257
bool motorWasNotReady
Definition: Axis.h:248
uint16_t lastdegreesOfRotation
Definition: Axis.h:209
float idlespringscale
Definition: Axis.h:247
void usbSuspend()
Definition: Axis.cpp:517
const Error outOfBoundsError
Definition: Axis.h:193
Biquad speedFilter
Definition: Axis.h:264
void setPower(uint16_t power)
Definition: Axis.cpp:360
MotorDriver * getDriver()
Definition: Axis.cpp:279
virtual ~Axis()
Definition: Axis.cpp:123
static const std::vector< class_entry< MotorDriver > > axis1_drivers
Definition: Axis.h:171
AxisConfig conf
Definition: Axis.h:183
static const std::vector< class_entry< MotorDriver > > axis2_drivers
Definition: Axis.h:172
void updateMetrics(float new_pos)
Definition: Axis.cpp:636
static uint16_t encodeConfToInt(AxisConfig conf)
Definition: Axis.cpp:1021
void resetMetrics(float new_pos)
Definition: Axis.cpp:623
int16_t updateEndstop()
Definition: Axis.cpp:677
const int32_t intFxClip
Definition: Axis.h:258
Biquad damperFilter
Definition: Axis.h:266
void registerCommands()
Definition: Axis.cpp:133
Encoder * getEncoder()
Definition: Axis.cpp:283
void setupTMC4671()
Definition: Axis.cpp:418
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies)
Definition: Axis.cpp:788
void setFxRatio(uint8_t val)
Definition: Axis.cpp:615
ClassChooser< Encoder > enc_chooser
Definition: Axis.h:150
static ClassIdentifier info
Definition: Axis.h:104
const float endstopGain
Definition: Axis.h:242
void setEncType(uint8_t enctype)
Definition: Axis.cpp:440
Biquad frictionFilter
Definition: Axis.h:267
uint16_t power
Definition: Axis.h:237
Biquad accelFilter
Definition: Axis.h:265
metric_t * getMetrics()
Definition: Axis.cpp:536
void setPos(uint16_t val)
Definition: Axis.cpp:271
float speedLimiterP
Definition: Axis.h:216
bool outOfBounds
Definition: Axis.h:188
std::shared_ptr< Encoder > enc
Definition: Axis.h:186
int32_t axisEffectTorque
Definition: Axis.h:235
bool invertAxis
Definition: Axis.h:240
int32_t getLastScaledEnc()
Definition: Axis.cpp:543
void restoreFlash() override
Definition: Axis.cpp:165
std::unique_ptr< MotorDriver > drv
Definition: Axis.h:185
uint8_t inertiaIntensity
Definition: Axis.h:262
void setGearRatio(uint8_t numerator, uint8_t denominator)
Definition: Axis.cpp:464
uint16_t degreesOfRotation
Definition: Axis.h:208
int32_t effectTorque
Definition: Axis.h:234
int16_t idlespringclip
Definition: Axis.h:246
void updateDriveTorque()
Definition: Axis.cpp:350
void setDrvType(uint8_t drvtype)
Definition: Axis.cpp:380
void saveFlash() override
Definition: Axis.cpp:240
float spdlimitreducerI
Definition: Axis.h:219
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition: Axis.cpp:128
void usbResume()
Definition: Axis.cpp:527
void emergencyStop(bool reset)
Definition: Axis.cpp:505
const float AXIS_DAMPER_RATIO
Definition: Axis.h:176
static AxisConfig decodeConfFromInt(uint16_t val)
Definition: Axis.cpp:1012
TMC4671Limits tmclimits
Definition: Axis.h:199
const std::array< biquad_constant_t, 4 > filterSpeedCst
Definition: Axis.h:251
uint8_t fx_ratio_i
Definition: Axis.h:236
uint32_t maxTorqueRateMS
Definition: Axis.h:215
const float AXIS_INERTIA_RATIO
Definition: Axis.h:177
uint16_t maxSpeedDegS
Definition: Axis.h:213
void updateTorqueScaler()
Definition: Axis.cpp:658
void prepareForUpdate()
Definition: Axis.cpp:290
uint8_t getDrvType()
Definition: Axis.cpp:259
float _lastSpeed
Definition: Axis.h:233
Biquad inertiaFilter
Definition: Axis.h:268
Definition: Filters.h:31
void setFc(float Fc)
Definition: Filters.cpp:28
float process(float in)
Definition: Filters.cpp:53
void setQ(float Q)
Definition: Filters.cpp:41
void calcBiquad(void)
Definition: Filters.cpp:72
void registerCommand(const char *cmd, const ID cmdid, const char *help=nullptr, uint32_t flags=0)
static CommandStatus handleGetSet(const ParsedCommand &cmd, std::vector< CommandReply > &replies, TVal &value)
void setInstance(uint8_t instance)
static CommandStatus handleGetSetFunc(const ParsedCommand &cmd, std::vector< CommandReply > &replies, TVal &value, void(cls1::*setfunc)(TVal), cls *obj)
static void addError(const Error &error)
virtual void setEncoder(std::shared_ptr< Encoder > &encoder)
Definition: MotorDriver.h:41
virtual void startMotor()
Definition: MotorDriver.cpp:97
virtual bool hasIntegratedEncoder()
Definition: MotorDriver.cpp:75
T clip(T v, C l, C h)
Definition: cppmain.h:58
bool Flash_Write(uint16_t adr, uint16_t dat)
bool Flash_Read(uint16_t adr, uint16_t *buf, bool checkempty=true)
void pulseClipLed()
Definition: ledEffects.cpp:49
void pulseErrLed()
Definition: ledEffects.cpp:44
uint8_t drvtype
Definition: Axis.h:66
uint8_t enctype
Definition: Axis.h:67
uint16_t config
Definition: Axis.h:50
uint16_t endstop
Definition: Axis.h:54
uint16_t maxSpeed
Definition: Axis.h:51
uint16_t degrees
Definition: Axis.h:56
uint16_t encoderRatio
Definition: Axis.h:59
uint16_t speedAccelFilter
Definition: Axis.h:61
uint16_t effects2
Definition: Axis.h:58
uint16_t effects1
Definition: Axis.h:57
uint16_t power
Definition: Axis.h:55
const char * name
Definition: Axis.h:38
bool usb_disabled
Definition: Axis.h:40
bool emergency
Definition: Axis.h:39
uint8_t denominator
Definition: Axis.h:86
uint8_t numerator
Definition: Axis.h:87
float gearRatio
Definition: Axis.h:88
uint32_t cmdId
uint16_t pid_torque_flux
Definition: TMC4671.h:182
metric_t current
Definition: Axis.h:81
metric_t previous
Definition: Axis.h:82
Definition: Axis.h:70
float posDegrees
Definition: Axis.h:75
int32_t torque
Definition: Axis.h:76
float accel
Definition: Axis.h:71
float speed
Definition: Axis.h:72
int32_t pos
Definition: Axis.h:73
float pos_f
Definition: Axis.h:74
bool tud_connected(void)
Definition: usbd.c:411