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,ADR_AXIS1_POSTPROCESS1});
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,ADR_AXIS2_POSTPROCESS1});
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,ADR_AXIS3_POSTPROCESS1});
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 registerCommand("expo", Axis_commands::expo, "Exponential curve correction (x^(val/exposcale)+1)", CMDFLAG_GET | CMDFLAG_SET);
161 registerCommand("exposcale", Axis_commands::exposcale, "Scaler constant for expo", CMDFLAG_GET);
162}
163
164/*
165 * Read parameters from flash and restore settings
166 */
168 //NormalizedAxis::restoreFlash();
169 // read all constants
170 uint16_t value;
171 if (Flash_Read(flashAddrs.config, &value)){
172 this->conf = Axis::decodeConfFromInt(value);
173 }else{
174 pulseErrLed();
175 }
176
177 setDrvType(this->conf.drvtype);
178 setEncType(this->conf.enctype);
179
180 if (Flash_Read(flashAddrs.maxSpeed, &value)){
181 this->maxSpeedDegS = value;
182 }else{
183 pulseErrLed();
184 }
185//
186// if (Flash_Read(flashAddrs.maxAccel, &value)){
187// this->maxTorqueRateMS = value;
188// }else{
189// pulseErrLed();
190// }
191
192
193 uint16_t esval, power;
194 if(Flash_Read(flashAddrs.endstop, &esval)) {
195 fx_ratio_i = esval & 0xff;
196 endstopStrength = (esval >> 8) & 0xff;
197 }
198
199
202 }
203 uint16_t deg_t;
204 if(Flash_Read(flashAddrs.degrees, &deg_t)){
205 this->degreesOfRotation = deg_t & 0x7fff;
206 this->invertAxis = (deg_t >> 15) & 0x1;
208 }
209
210
211 uint16_t effects;
215 }else{
217 }
218
222 }
223
224 uint16_t ratio;
226 setGearRatio(ratio & 0xff, (ratio >> 8) & 0xff);
227 }
228
229 uint16_t filterStorage;
230 if (Flash_Read(flashAddrs.speedAccelFilter, &filterStorage))
231 {
232 uint8_t profile = filterStorage & 0xFF;
233 this->filterProfileId = profile;
238 }
239
240 uint16_t pp1;
242 setExpo((int8_t)(pp1 & 0xff));
243 }
244
245}
246// Saves parameters to flash.
248 //NormalizedAxis::saveFlash();
250 Flash_Write(flashAddrs.maxSpeed, this->maxSpeedDegS);
251// Flash_Write(flashAddrs.maxAccel, (uint16_t)(this->maxTorqueRateMS));
252
259
260 // save CF biquad
261 uint16_t filterStorage = (uint16_t)this->filterProfileId & 0xFF;
263
264 // Postprocessing
266
267}
268
269
271 return (uint8_t)this->conf.drvtype;
272}
273
275 if(drv->hasIntegratedEncoder()){
276 return 255;
277 }
278 return (uint8_t)this->conf.enctype;
279}
280
281
282void Axis::setPos(uint16_t val)
283{
284 startForceFadeIn(0.25,0.5);
285 if(this->drv != nullptr){
286 drv->getEncoder()->setPos(val);
287 }
288}
289
291 return drv.get();
292}
293
295 return drv->getEncoder();
296}
297
302 if (drv == nullptr){
303 pulseErrLed();
304 return;
305 }
306
307 //if (!drv->motorReady()) return;
308
309 float angle = getEncAngle(this->drv->getEncoder());
310
311 // Scale encoder value to set rotation range
312 // Update a change of range only when new range is within valid range
313 // if degree change, compute the SpeedScaler, it depends on degreesOfRotation
315 int32_t scaledEnc;
316 std::tie(scaledEnc,std::ignore) = scaleEncValue(angle, nextDegreesOfRotation);
317 if(abs(scaledEnc) < 0x7fff){
319 }
320
321 }
322
323
324 // scaledEnc now gets inverted if necessary in updateMetrics
325 int32_t scaledEnc;
326 std::tie(scaledEnc,std::ignore) = scaleEncValue(angle, degreesOfRotation);
327
328 if (abs(scaledEnc) > 0xffff && drv->motorReady()){
329 // We are way off. Shut down
330 drv->stopMotor();
331 pulseErrLed();
332 if(!outOfBounds){
333 outOfBounds = true;
335 }
336
337 }else if(abs(scaledEnc) <= 0x7fff) {
338 outOfBounds = false;
339 //ErrorHandler::clearError(outOfBoundsError);
340 }
341
342 // On first change to ready start a fade
343 if(motorWasNotReady && drv->motorReady()){
344 motorWasNotReady = false;
345 startForceFadeIn(0, 1.0);
346 }
347
348
349 this->updateMetrics(angle);
350
351}
352
353void Axis::errorCallback(const Error &error, bool cleared){
354 if(cleared && error == this->outOfBoundsError){
355 drv->startMotor();
356 outOfBounds = false;
357 }
358}
359
360
362 // totalTorque = effectTorque + endstopTorque
363 int32_t totalTorque;
364 bool torqueChanged = updateTorque(&totalTorque);
365 if (torqueChanged && drv->motorReady()){
366 // Send to motor driver
367 drv->turn(totalTorque);
368 }
369}
370
371void Axis::setPower(uint16_t power)
372{
373 this->power = power;
375#ifdef TMC4671DRIVER
376 // Update hardware limits for TMC for safety
377 TMC4671 *drv = dynamic_cast<TMC4671 *>(this->drv.get());
378 if (drv != nullptr)
379 {
380 //tmclimits.pid_uq_ud = power;
381 //tmclimits.pid_torque_flux = power;
382 drv->setTorqueLimit(power);
383 }
384#endif
385}
386
387
391void Axis::setDrvType(uint8_t drvtype)
392{
393 if (!drv_chooser.isValidClassId(drvtype))
394 {
395 return;
396 }
397 this->drv.reset(nullptr);
398 MotorDriver* drv = drv_chooser.Create((uint16_t)drvtype);
399 if (drv == nullptr)
400 {
401 return;
402 }
403 this->drv = std::unique_ptr<MotorDriver>(drv);
404 this->conf.drvtype = drvtype;
405
406 // Pass encoder to driver again
407 if(!this->drv->hasIntegratedEncoder()){
408 this->drv->setEncoder(this->enc);
409 }
410#ifdef TMC4671DRIVER
411 if (dynamic_cast<TMC4671 *>(drv))
412 {
413 setupTMC4671();
414 }
415#endif
416 if (!tud_connected())
417 {
418 control->usb_disabled = false;
419 this->usbSuspend();
420 }
421 else
422 {
423 drv->startMotor();
424 }
425}
426
427#ifdef TMC4671DRIVER
428// Special tmc setup methods
430{
431 TMC4671 *drv = static_cast<TMC4671 *>(this->drv.get());
432// drv->setAxis(axis);
433 drv->setExternalEncoderAllowed(true);
434 drv->restoreFlash();
436 drv->setLimits(tmclimits);
437 //drv->setBiquadTorque(TMC4671Biquad(tmcbq_500hz_07q_25k));
438
439
440 // Enable driver
441
442 drv->setMotionMode(MotionMode::torque);
443 drv->Start(); // Start thread
444}
445#endif
446
447
451void Axis::setEncType(uint8_t enctype)
452{
453 if (enc_chooser.isValidClassId(enctype) && !drv->hasIntegratedEncoder())
454 {
455
456 this->conf.enctype = (enctype);
457 this->enc = std::shared_ptr<Encoder>(enc_chooser.Create(enctype)); // Make new encoder
458 if(drv && !drv->hasIntegratedEncoder())
459 this->drv->setEncoder(this->enc);
460 }else{
461 this->conf.enctype = 0; // None encoder
462 }
463
464 float angle = getEncAngle(this->drv->getEncoder());
465 //int32_t scaledEnc = scaleEncValue(angle, degreesOfRotation);
466 // reset metrics
467 this->resetMetrics(angle);
468
469}
470
475void Axis::setGearRatio(uint8_t numerator,uint8_t denominator){
476 this->gearRatio.denominator = denominator;
477 this->gearRatio.numerator = numerator;
478 this->gearRatio.gearRatio = ((float)numerator+1.0)/((float)denominator+1.0);
479}
480
486std::pair<int32_t,float> Axis::scaleEncValue(float angle, uint16_t degrees){
487 if (degrees == 0){
488 return std::make_pair<int32_t,float>(0x7fff,0.0);
489 }
490
491 int32_t val = (0xffff / (float)degrees) * angle;
492 float val_f = (2.0 / (float)degrees) * angle;
493
494 return std::make_pair(val,val_f);
495}
496
501 if(enc != nullptr){
502 float pos = 360.0 * enc->getPos_f() * gearRatio.gearRatio;
503 if (isInverted()){
504 pos= -pos;
505 }
506 return pos;
507 }
508 else{
509 return 0;
510 }
511}
512
516void Axis::emergencyStop(bool reset){
517 drv->turn(0); // Send 0 torque first
518 if(reset){
520 }
521 drv->emergencyStop(reset);
522 control->emergency = !reset;
523}
524
529 if (drv != nullptr){
530 drv->turn(0);
531 drv->stopMotor();
532 }
533}
534
540 if (drv != nullptr){
541 drv->startMotor();
542 }
543}
544
545
546
548 return &metric.current;
549}
550
555 return clip(metric.current.pos,-0x7fff,0x7fff);
556}
557
562 return clip<int32_t,int32_t>((int32_t)(-metric.current.pos*idlespringscale),-idlespringclip,idlespringclip);
563}
564
565/*
566 * Set the strength of the spring effect if FFB is disabled
567 */
568void Axis::setIdleSpringStrength(uint8_t spring){
570 idlespringclip = clip<int32_t,int32_t>((int32_t)spring*35,0,10000);
571 idlespringscale = 0.5f + ((float)spring * 0.01f);
572}
573
577void Axis::setFxStrengthAndFilter(uint8_t val,uint8_t& valToSet, Biquad& filter){
578 if(valToSet == 0 && val != 0)
579 filter.calcBiquad();
580
581 valToSet = val;
582}
583
590
591 if(!ffb_on){
593 }
594
595 // Always active damper
596 if(damperIntensity != 0){
597 float speedFiltered = (metric.current.speed) * (float)damperIntensity * AXIS_DAMPER_RATIO;
598 axisEffectTorque -= damperFilter.process(clip<float, int32_t>(speedFiltered, -intFxClip, intFxClip));
599 }
600
601 // Always active inertia
602 if(inertiaIntensity != 0){
603 float accelFiltered = metric.current.accel * (float)inertiaIntensity * AXIS_INERTIA_RATIO;
604 axisEffectTorque -= inertiaFilter.process(clip<float, int32_t>(accelFiltered, -intFxClip, intFxClip));
605 }
606
607 // Always active friction. Based on effectsCalculator implementation
608 if(frictionIntensity != 0){
609 float speed = metric.current.speed * INTERNAL_SCALER_FRICTION;
610 float speedRampupCeil = 4096;
611 float rampupFactor = 1.0;
612 if (fabs (speed) < speedRampupCeil) { // if speed in the range to rampup we apply a sine curve
613 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
614 rampupFactor = ( 1 + sin(phaseRad ) ) / 2; // sin value is -1..1 range, we translate it to 0..2 and we scale it by 2
615 }
616 int8_t sign = speed >= 0 ? 1 : -1;
617 float force = (float)frictionIntensity * rampupFactor * sign * INTERNAL_AXIS_FRICTION_SCALER * 32;
618 axisEffectTorque -= frictionFilter.process(clip<float, int32_t>(force, -intFxClip, intFxClip));
619 }
620
621}
622
626void Axis::setFxRatio(uint8_t val) {
627 fx_ratio_i = val;
629}
630
634void Axis::resetMetrics(float new_pos= 0) { // pos is degrees
636 metric.current.posDegrees = new_pos;
639 // Reset filters
642}
643
647void Axis::updateMetrics(float new_pos) { // pos is degrees
648 // store old value for next metric's computing
650
651 metric.current.posDegrees = new_pos;
653
654
655 // compute speed and accel from raw instant speed normalized
656 float currentSpeed = (new_pos - metric.previous.posDegrees) * 1000.0; // deg/s
657 metric.current.speed = speedFilter.process(currentSpeed);
658 metric.current.accel = accelFilter.process((currentSpeed - _lastSpeed))* 1000.0; // deg/s/s
659 _lastSpeed = currentSpeed;
660
661}
662
663
664
665uint16_t Axis::getPower(){
666 return power;
667}
668
672int32_t Axis::calculateExpoTorque(int32_t torque){
673 float torquef = (float)torque / (float)0x7fff; // This down and upscaling may introduce float artifacts. Do this before scaling down.
674 if(torquef < 0){
675 return -powf(-torquef,expo) * 0x7fff;
676 }else{
677 return powf(torquef,expo) * 0x7fff;
678 }
679}
680
682 effect_margin_scaler = ((float)fx_ratio_i/255.0);
683 torqueScaler = ((float)power / (float)0x7fff);
684}
685
687 return torqueScaler;
688}
689
690
692
694 return invertAxis;
695}
696
701 int8_t clipdir = cliptest<int32_t,int32_t>(metric.current.pos, -0x7fff, 0x7fff);
702 if(clipdir == 0){
703 return 0;
704 }
705 float addtorque = clipdir*metric.current.posDegrees - (float)this->degreesOfRotation/2.0; // degress of rotation counts total range so multiply by 2
706 addtorque *= (float)endstopStrength * endstopGain; // Apply endstop gain for stiffness.
707 addtorque *= -clipdir;
708
709 return clip<int32_t,int32_t>(addtorque,-0x7fff,0x7fff);
710}
711
712void Axis::setEffectTorque(int32_t torque) {
714}
715
720bool Axis::updateTorque(int32_t* totalTorque) {
721
722 if(abs(effectTorque) >= 0x7fff){
723 pulseClipLed();
724 }
725
726 // Scale effect torque
727 int32_t torque = effectTorque; // Game effects
728 if(expo != 1){
730 }
732 torque += axisEffectTorque; // Independent effects
734 torque *= torqueScaler; // Scale to power
735
736
737 // TODO speed and accel limiters
738 if(maxSpeedDegS > 0){
739
740 float torqueSign = torque > 0 ? 1 : -1; // Used to prevent metrics against the force to go into the limiter
741 // Speed. Mostly tuned...
742 //spdlimiterAvg.addValue(metric.current.speed);
743 float speedreducer = (float)((metric.current.speed*torqueSign) - (float)maxSpeedDegS) * ((float)0x7FFF / maxSpeedDegS);
744 spdlimitreducerI = clip<float,int32_t>( spdlimitreducerI + ((speedreducer * speedLimiterI) * torqueScaler),0,power);
745
746 // Accel limit. Not really useful. Maybe replace with torque slew rate limit?
747// float accreducer = (float)((metric.current.accel*torqueSign) - (float)maxAccelDegSS) * getAccelScalerNormalized();
748// acclimitreducerI = clip<float,int32_t>( acclimitreducerI + ((accreducer * 0.02) * torqueScaler),0,power);
749
750
751 // Only reduce torque. Don't invert it to prevent oscillation
752 float torqueReduction = speedreducer * speedLimiterP + spdlimitreducerI;// accreducer * 0.025 + acclimitreducerI
753 if(torque > 0){
754 torqueReduction = clip<float,int32_t>(torqueReduction,0,torque);
755 }else{
756 torqueReduction = clip<float,int32_t>(-torqueReduction,torque,0);
757 }
758
759 torque -= torqueReduction;
760 }
761 // Torque slew rate limiter
762 if(maxTorqueRateMS > 0){
764 }
765// if(torque - metric.previous.torque)
766 if(outOfBounds){
767 torque = 0;
768 }
769
770 // Fade in
771 if(forceFadeCurMult < 1){
773 forceFadeCurMult += forceFadeTime / this->filter_f; // Fade time
774 }
775
776 // Torque calculated. Now sending to driver
777 torque = (invertAxis) ? -torque : torque;
779 torque = clip<int32_t, int32_t>(torque, -power, power);
780
781 bool torqueChanged = metric.current.torque != metric.previous.torque;
782
783 if (abs(torque) == power){
784 pulseClipLed();
785 }
786
787 *totalTorque = torque;
788 return (torqueChanged);
789}
790
794void Axis::startForceFadeIn(float start,float fadeTime){
795 this->forceFadeTime = fadeTime;
796 this->forceFadeCurMult = clip<float>(start, 0, 1);
797}
798
799
803void Axis::setDegrees(uint16_t degrees){
804
805 degrees &= 0x7fff;
806 if(degrees == 0){
808 }else{
811 }
812}
813
814
815void Axis::setExpo(int val){
816 val = clip(val, -127, 127);
817 expoValInt = val;
818 if(val == 0){
819 expo = 1; // Explicitly force expo off
820 return;
821 }
822 float valF = abs((float)val / expoScaler);
823 if(val < 0){
824 expo = 1.0f/(1.0f+valF);
825 }else{
826 expo = 1+valF;
827 }
828}
829
830CommandStatus Axis::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
831
832 switch(static_cast<Axis_commands>(cmd.cmdId)){
833
835 if (cmd.type == CMDtype::get)
836 {
837 replies.emplace_back(this->power);
838 }
839 else if (cmd.type == CMDtype::set)
840 {
841 setPower(cmd.val);
842 }
843 break;
844
847// if (cmd.type == CMDtype::get)
848// {
849// replies.emplace_back(degreesOfRotation);
850// }
851// else if (cmd.type == CMDtype::set)
852// {
853// setDegrees(cmd.val);
854// }
855 break;
856
858 handleGetSet(cmd, replies, this->endstopStrength);
859 break;
860
862 this->setPos(0);
863 break;
864
866 if (cmd.type == CMDtype::get)
867 {
868 replies.emplace_back(invertAxis ? 1 : 0);
869 }
870 else if (cmd.type == CMDtype::set)
871 {
872 invertAxis = cmd.val >= 1 ? true : false;
874 }
875 break;
876
878 if (cmd.type == CMDtype::get)
879 {
880 replies.emplace_back(idlespringstrength);
881 }
882 else if (cmd.type == CMDtype::set)
883 {
885 }
886 break;
887
889 if (cmd.type == CMDtype::get)
890 {
891 replies.emplace_back(damperIntensity);
892 }
893 else if (cmd.type == CMDtype::set)
894 {
895 setFxStrengthAndFilter(cmd.val,this->damperIntensity,this->damperFilter);
896 }
897 break;
898
900 if (cmd.type == CMDtype::get)
901 {
902 replies.emplace_back(frictionIntensity);
903 }
904 else if (cmd.type == CMDtype::set)
905 {
906 setFxStrengthAndFilter(cmd.val,this->frictionIntensity,this->frictionFilter);
907 }
908 break;
909
911 if (cmd.type == CMDtype::get)
912 {
913 replies.emplace_back(inertiaIntensity);
914 }
915 else if (cmd.type == CMDtype::set)
916 {
917 setFxStrengthAndFilter(cmd.val,this->inertiaIntensity,this->inertiaFilter);
918 }
919 break;
920
922 if(cmd.type == CMDtype::info){
923 enc_chooser.replyAvailableClasses(replies,this->getEncType());
924 }else if(cmd.type == CMDtype::get){
925 replies.emplace_back(this->getEncType());
926 }else if(cmd.type == CMDtype::set){
927 this->setEncType(cmd.val);
928 }
929 break;
930
932 if(cmd.type == CMDtype::info){
933 drv_chooser.replyAvailableClasses(replies,this->getDrvType());
934 }else if(cmd.type == CMDtype::get){
935 replies.emplace_back(this->getDrvType());
936 }else if(cmd.type == CMDtype::set){
937 this->setDrvType(cmd.val);
938 }
939 break;
940
942 if (cmd.type == CMDtype::get && this->drv->getEncoder() != nullptr)
943 {
944 int32_t pos = this->drv->getEncoder()->getPos();
945 replies.emplace_back(isInverted() ? -pos : pos);
946 }
947 else if (cmd.type == CMDtype::set && this->drv->getEncoder() != nullptr)
948 {
949 this->drv->getEncoder()->setPos(isInverted() ? -cmd.val : cmd.val);
950 }
951 else
952 {
953 return CommandStatus::ERR;
954 }
955 break;
956
958 handleGetSet(cmd, replies, this->maxSpeedDegS);
959 break;
960
962 handleGetSet(cmd, replies, this->maxTorqueRateMS);
963 break;
964
966 if(cmd.type == CMDtype::get){
967 replies.emplace_back(this->fx_ratio_i);
968 }else if(cmd.type == CMDtype::set){
969 setFxRatio(cmd.val);
970 }
971 break;
972
974 replies.emplace_back(this->metric.current.pos);
975 break;
977 replies.emplace_back(this->metric.current.torque);
978 break;
980 replies.emplace_back(this->metric.current.speed);
981 break;
983 replies.emplace_back(this->metric.current.accel);
984 break;
985
987 if(cmd.type == CMDtype::get){
988 replies.emplace_back(gearRatio.numerator+1,gearRatio.denominator+1);
989 }else if(cmd.type == CMDtype::setat){
990 setGearRatio(cmd.val-1,cmd.adr-1);
991 }
992 break;
993
995 if (cmd.type == CMDtype::get)
996 {
997 replies.emplace_back(this->filterProfileId);
998 }
999 else if (cmd.type == CMDtype::set)
1000 {
1001 uint32_t value = clip<uint32_t, uint32_t>(cmd.val, 0, filterSpeedCst.size()-1);
1002 this->filterProfileId = value;
1007 }
1008 break;
1010 if (cmd.type == CMDtype::get)
1011 {
1012 replies.emplace_back(this->filterSpeedCst[this->filterProfileId].freq,this->filterSpeedCst[this->filterProfileId].q);
1013 }
1014 break;
1015
1017 if (cmd.type == CMDtype::get)
1018 {
1019 replies.emplace_back(this->filterAccelCst[this->filterProfileId].freq,this->filterAccelCst[this->filterProfileId].q);
1020 }
1021 break;
1022
1023 case Axis_commands::cpr:
1024 if (cmd.type == CMDtype::get)
1025 {
1026 uint32_t cpr = 0;
1027 if(this->drv->getEncoder() != nullptr){
1028 cpr = this->drv->getEncoder()->getCpr();
1029 }
1030//#ifdef TMC4671DRIVER // CPR should be consistent with position. Maybe change TMC to prescale to encoder count or correct readout in UI
1031// TMC4671 *tmcdrv = dynamic_cast<TMC4671 *>(this->drv.get()); // Special case for TMC. Get the actual encoder resolution
1032// if (tmcdrv && tmcdrv->hasIntegratedEncoder())
1033// {
1034// cpr = tmcdrv->getEncCpr();
1035// }
1036//#endif
1037 replies.emplace_back(cpr);
1038 }else{
1039 return CommandStatus::ERR;
1040 }
1041 break;
1042
1044 handleGetSetFunc(cmd, replies, expoValInt, &Axis::setExpo, this); // need to also provide the expoScaler constant
1045 break;
1046
1048 handleGetSet(cmd, replies, expoScaler); // need to also provide the expoScaler constant
1049 break;
1050
1051 default:
1053 }
1054 return CommandStatus::OK;
1055}
1056
1057
1058
1059/*
1060 * Helper functions for encoding and decoding flash variables
1061 */
1063{
1064 // 0-6 enc, 7-12 Mot
1066 conf.enctype = ((val)&0x3f);
1067 conf.drvtype = ((val >> 6) & 0x3f);
1068 return conf;
1069}
1070
1072{
1073 uint16_t val = (uint8_t)conf.enctype & 0x3f;
1074 val |= ((uint8_t)conf.drvtype & 0x3f) << 6;
1075 return val;
1076}
Axis_commands
Definition: Axis.h:93
CommandStatus
float forceFadeTime
Definition: Axis.h:200
float effect_margin_scaler
Definition: Axis.h:244
float getTorqueScaler()
Definition: Axis.cpp:686
void errorCallback(const Error &error, bool cleared) override
Definition: Axis.cpp:353
const std::array< biquad_constant_t, 4 > filterAccelCst
Definition: Axis.h:257
float getEncAngle(Encoder *enc)
Definition: Axis.cpp:500
uint8_t idlespringstrength
Definition: Axis.h:250
float speedLimiterI
Definition: Axis.h:222
AxisFlashAddrs flashAddrs
Definition: Axis.h:184
void startForceFadeIn(float start=0, float fadeTime=0.5)
Definition: Axis.cpp:794
int32_t getTorque()
Definition: Axis.cpp:691
axis_metric_t metric
Definition: Axis.h:237
void setEffectTorque(int32_t torque)
Definition: Axis.cpp:712
GearRatio_t gearRatio
Definition: Axis.h:282
bool isInverted()
Definition: Axis.cpp:693
uint16_t nextDegreesOfRotation
Definition: Axis.h:215
Axis(char axis, volatile Control_t *control)
Definition: Axis.cpp:88
uint8_t endstopStrength
Definition: Axis.h:246
float forceFadeCurMult
Definition: Axis.h:201
float torqueScaler
Definition: Axis.h:243
char axis
Definition: Axis.h:233
ClassChooser< MotorDriver > drv_chooser
Definition: Axis.h:151
uint8_t frictionIntensity
Definition: Axis.h:266
void setFxStrengthAndFilter(uint8_t val, uint8_t &valToSet, Biquad &filter)
Definition: Axis.cpp:577
int32_t updateIdleSpringForce()
Definition: Axis.cpp:561
void setDegrees(uint16_t degrees)
Definition: Axis.cpp:803
void calculateAxisEffects(bool ffb_on)
Definition: Axis.cpp:588
bool updateTorque(int32_t *totalTorque)
Definition: Axis.cpp:720
uint8_t damperIntensity
Definition: Axis.h:264
uint8_t filterProfileId
Definition: Axis.h:261
uint16_t getPower()
Definition: Axis.cpp:665
uint8_t getEncType()
Definition: Axis.cpp:274
volatile Control_t * control
Definition: Axis.h:185
std::pair< int32_t, float > scaleEncValue(float angle, uint16_t degrees)
Definition: Axis.cpp:486
void setIdleSpringStrength(uint8_t spring)
Definition: Axis.cpp:568
const float filter_f
Definition: Axis.h:262
bool motorWasNotReady
Definition: Axis.h:253
uint16_t lastdegreesOfRotation
Definition: Axis.h:214
float idlespringscale
Definition: Axis.h:252
void usbSuspend()
Definition: Axis.cpp:528
const Error outOfBoundsError
Definition: Axis.h:198
Biquad speedFilter
Definition: Axis.h:269
void setPower(uint16_t power)
Definition: Axis.cpp:371
MotorDriver * getDriver()
Definition: Axis.cpp:290
virtual ~Axis()
Definition: Axis.cpp:123
static const std::vector< class_entry< MotorDriver > > axis1_drivers
Definition: Axis.h:176
AxisConfig conf
Definition: Axis.h:188
static const std::vector< class_entry< MotorDriver > > axis2_drivers
Definition: Axis.h:177
void updateMetrics(float new_pos)
Definition: Axis.cpp:647
static uint16_t encodeConfToInt(AxisConfig conf)
Definition: Axis.cpp:1071
void resetMetrics(float new_pos)
Definition: Axis.cpp:634
int16_t updateEndstop()
Definition: Axis.cpp:700
const int32_t intFxClip
Definition: Axis.h:263
Biquad damperFilter
Definition: Axis.h:271
void registerCommands()
Definition: Axis.cpp:133
Encoder * getEncoder()
Definition: Axis.cpp:294
void setupTMC4671()
Definition: Axis.cpp:429
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies)
Definition: Axis.cpp:830
void setFxRatio(uint8_t val)
Definition: Axis.cpp:626
ClassChooser< Encoder > enc_chooser
Definition: Axis.h:152
static ClassIdentifier info
Definition: Axis.h:106
const float endstopGain
Definition: Axis.h:247
void setEncType(uint8_t enctype)
Definition: Axis.cpp:451
Biquad frictionFilter
Definition: Axis.h:272
uint16_t power
Definition: Axis.h:242
Biquad accelFilter
Definition: Axis.h:270
metric_t * getMetrics()
Definition: Axis.cpp:547
void setPos(uint16_t val)
Definition: Axis.cpp:282
float expo
Definition: Axis.h:285
float speedLimiterP
Definition: Axis.h:221
bool outOfBounds
Definition: Axis.h:193
int32_t calculateExpoTorque(int32_t torque)
Definition: Axis.cpp:672
std::shared_ptr< Encoder > enc
Definition: Axis.h:191
float expoScaler
Definition: Axis.h:286
int32_t axisEffectTorque
Definition: Axis.h:240
bool invertAxis
Definition: Axis.h:245
int32_t getLastScaledEnc()
Definition: Axis.cpp:554
void restoreFlash() override
Definition: Axis.cpp:167
std::unique_ptr< MotorDriver > drv
Definition: Axis.h:190
uint8_t inertiaIntensity
Definition: Axis.h:267
void setGearRatio(uint8_t numerator, uint8_t denominator)
Definition: Axis.cpp:475
uint16_t degreesOfRotation
Definition: Axis.h:213
void setExpo(int val)
Definition: Axis.cpp:815
int32_t effectTorque
Definition: Axis.h:239
int16_t idlespringclip
Definition: Axis.h:251
void updateDriveTorque()
Definition: Axis.cpp:361
void setDrvType(uint8_t drvtype)
Definition: Axis.cpp:391
void saveFlash() override
Definition: Axis.cpp:247
float spdlimitreducerI
Definition: Axis.h:224
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition: Axis.cpp:128
void usbResume()
Definition: Axis.cpp:538
void emergencyStop(bool reset)
Definition: Axis.cpp:516
const float AXIS_DAMPER_RATIO
Definition: Axis.h:181
static AxisConfig decodeConfFromInt(uint16_t val)
Definition: Axis.cpp:1062
TMC4671Limits tmclimits
Definition: Axis.h:204
int expoValInt
Definition: Axis.h:284
const std::array< biquad_constant_t, 4 > filterSpeedCst
Definition: Axis.h:256
uint8_t fx_ratio_i
Definition: Axis.h:241
uint32_t maxTorqueRateMS
Definition: Axis.h:220
const float AXIS_INERTIA_RATIO
Definition: Axis.h:182
uint16_t maxSpeedDegS
Definition: Axis.h:218
void updateTorqueScaler()
Definition: Axis.cpp:681
void prepareForUpdate()
Definition: Axis.cpp:301
uint8_t getDrvType()
Definition: Axis.cpp:270
float _lastSpeed
Definition: Axis.h:238
Biquad inertiaFilter
Definition: Axis.h:273
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:67
uint8_t enctype
Definition: Axis.h:68
uint16_t config
Definition: Axis.h:50
uint16_t endstop
Definition: Axis.h:54
uint16_t postprocess1
Definition: Axis.h:62
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:87
uint8_t numerator
Definition: Axis.h:88
float gearRatio
Definition: Axis.h:89
uint32_t cmdId
uint16_t pid_torque_flux
Definition: TMC4671.h:182
metric_t current
Definition: Axis.h:82
metric_t previous
Definition: Axis.h:83
Definition: Axis.h:71
float posDegrees
Definition: Axis.h:76
int32_t torque
Definition: Axis.h:77
float accel
Definition: Axis.h:72
float speed
Definition: Axis.h:73
int32_t pos
Definition: Axis.h:74
float pos_f
Definition: Axis.h:75
bool tud_connected(void)
Definition: usbd.c:411