Open FFBoard
Open source force feedback firmware
Loading...
Searching...
No Matches
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#include "critical.hpp"
17
19/*
20 * Sources for class choosers are defined in MotorDriver and Encoder
21 */
22
23
25
27 .name = "Axis",
28 .id = CLSID_AXIS, // 1
29 .visibility = ClassVisibility::visible};
30
34const std::vector<class_entry<MotorDriver>> Axis::axis1_drivers =
35{
37
38#ifdef TMC4671DRIVER
40#endif
41#ifdef PWMDRIVER
43#endif
44#ifdef ODRIVE
46#endif
47#ifdef VESC
49#endif
50#ifdef SIMPLEMOTION
52#endif
53#ifdef RMDCAN
55#endif
56};
57
61const std::vector<class_entry<MotorDriver>> Axis::axis2_drivers =
62{
64
65#ifdef TMC4671DRIVER
67#endif
68#ifdef PWMDRIVER
70#endif
71#ifdef ODRIVE
73#endif
74#ifdef VESC
76#endif
77#ifdef RMDCAN
79#endif
80//#ifdef SIMPLEMOTION
81// add_class<MotorSimplemotion2,MotorDriver>(10), // TODO this likely does not work reliably with a single uart port and multiple devices
82//#endif
83};
84
85
89Axis::Axis(char axis,volatile Control_t* control) :CommandHandler("axis", CLSID_AXIS), drv_chooser(MotorDriver::all_drivers),enc_chooser{Encoder::all_encoders}
90{
91 this->axis = axis;
92 this->control = control;
93 if (axis == 'X')
94 {
96 setInstance(0);
97 this->flashAddrs = AxisFlashAddrs({ADR_AXIS1_CONFIG, ADR_AXIS1_MAX_SPEED, ADR_AXIS1_MAX_ACCEL,
98 ADR_AXIS1_ENDSTOP, ADR_AXIS1_POWER, ADR_AXIS1_DEGREES,ADR_AXIS1_EFFECTS1,ADR_AXIS1_EFFECTS2,ADR_AXIS1_ENC_RATIO,
99 ADR_AXIS1_SPEEDACCEL_FILTER,ADR_AXIS1_POSTPROCESS1});
100 }
101 else if (axis == 'Y')
102 {
104 setInstance(1);
105 this->flashAddrs = AxisFlashAddrs({ADR_AXIS2_CONFIG, ADR_AXIS2_MAX_SPEED, ADR_AXIS2_MAX_ACCEL,
106 ADR_AXIS2_ENDSTOP, ADR_AXIS2_POWER, ADR_AXIS2_DEGREES,ADR_AXIS2_EFFECTS1,ADR_AXIS2_EFFECTS2, ADR_AXIS2_ENC_RATIO,
107 ADR_AXIS2_SPEEDACCEL_FILTER,ADR_AXIS2_POSTPROCESS1});
108 }
109 else if (axis == 'Z')
110 {
111 setInstance(2);
112 this->flashAddrs = AxisFlashAddrs({ADR_AXIS3_CONFIG, ADR_AXIS3_MAX_SPEED, ADR_AXIS3_MAX_ACCEL,
113 ADR_AXIS3_ENDSTOP, ADR_AXIS3_POWER, ADR_AXIS3_DEGREES,ADR_AXIS3_EFFECTS1,ADR_AXIS3_EFFECTS2,ADR_AXIS3_ENC_RATIO,
114 ADR_AXIS3_SPEEDACCEL_FILTER,ADR_AXIS3_POSTPROCESS1});
115 }
116
117
118 restoreFlash(); // Load parameters
119 CommandHandler::registerCommands(); // Internal commands
121 updateTorqueScaler(); // In case no flash setting has been loaded yet
122}
123
125{
126
127}
128
130
131 return info;
132}
133
135 registerCommand("power", Axis_commands::power, "Overall force strength",CMDFLAG_GET | CMDFLAG_SET);
136 registerCommand("degrees", Axis_commands::degrees, "Rotation range in deg",CMDFLAG_GET | CMDFLAG_SET);
137 registerCommand("esgain", Axis_commands::esgain, "Endstop stiffness",CMDFLAG_GET | CMDFLAG_SET);
138 registerCommand("zeroenc", Axis_commands::zeroenc, "Zero axis",CMDFLAG_GET);
139 registerCommand("invert", Axis_commands::invert, "Invert axis",CMDFLAG_GET | CMDFLAG_SET);
140 registerCommand("idlespring", Axis_commands::idlespring, "Idle spring strength",CMDFLAG_GET | CMDFLAG_SET);
141 registerCommand("axisdamper", Axis_commands::axisdamper, "Independent damper effect",CMDFLAG_GET | CMDFLAG_SET);
142 registerCommand("axisfriction", Axis_commands::axisfriction, "Independent friction effect",CMDFLAG_GET | CMDFLAG_SET);
143 registerCommand("axisinertia", Axis_commands::axisinertia, "Independent inertia effect",CMDFLAG_GET | CMDFLAG_SET);
144 registerCommand("enctype", Axis_commands::enctype, "Encoder type get/set/list",CMDFLAG_GET | CMDFLAG_SET | CMDFLAG_INFOSTRING);
145 registerCommand("drvtype", Axis_commands::drvtype, "Motor driver type get/set/list",CMDFLAG_GET | CMDFLAG_SET | CMDFLAG_INFOSTRING);
146 registerCommand("pos", Axis_commands::pos, "Encoder position",CMDFLAG_GET);
147 registerCommand("maxspeed", Axis_commands::maxspeed, "Speed limit in deg/s",CMDFLAG_GET | CMDFLAG_SET);
148 registerCommand("maxtorquerate", Axis_commands::maxtorquerate, "Torque rate limit in counts/ms",CMDFLAG_GET | CMDFLAG_SET);
149 registerCommand("fxratio", Axis_commands::fxratio, "Effect ratio. Reduces game effects excluding endstop. 255=100%",CMDFLAG_GET | CMDFLAG_SET);
150 registerCommand("curtorque", Axis_commands::curtorque, "Axis torque",CMDFLAG_GET);
151 registerCommand("curpos", Axis_commands::curpos, "Axis position",CMDFLAG_GET);
152 registerCommand("curspd", Axis_commands::curspd, "Axis speed",CMDFLAG_GET);
153 registerCommand("curaccel", Axis_commands::curaccel, "Axis accel",CMDFLAG_GET);
154 registerCommand("reduction", Axis_commands::reductionScaler, "Encoder to axis gear reduction (val / adr) 1-256",CMDFLAG_GET | CMDFLAG_SETADR);
155 registerCommand("filterProfile_id", Axis_commands::filterProfileId, "Biquad filter profile for speed and accel", CMDFLAG_GET | CMDFLAG_SET);
156 //Can only read exact filter settings
157 registerCommand("filterSpeed", Axis_commands::filterSpeed, "Biquad filter freq and q*100 for speed", CMDFLAG_GET);
158 registerCommand("filterAccel", Axis_commands::filterAccel, "Biquad filter freq and q*100 for accel", CMDFLAG_GET);
159
160 registerCommand("cpr", Axis_commands::cpr, "Reported encoder CPR",CMDFLAG_GET);
161 registerCommand("expo", Axis_commands::expo, "Exponential curve correction (x^(val/exposcale)+1)", CMDFLAG_GET | CMDFLAG_SET);
162 registerCommand("exposcale", Axis_commands::exposcale, "Scaler constant for expo", CMDFLAG_GET);
163}
164
165/*
166 * Read parameters from flash and restore settings
167 */
169 //NormalizedAxis::restoreFlash();
170 // read all constants
171 uint16_t value;
172 if (Flash_Read(flashAddrs.config, &value)){
173 this->conf = Axis::decodeConfFromInt(value);
174 }else{
175 pulseErrLed();
176 }
177
178 setDrvType(this->conf.drvtype);
179 setEncType(this->conf.enctype);
180
181 if (Flash_Read(flashAddrs.maxSpeed, &value)){
182 this->maxSpeedDegS = value;
183 }else{
184 pulseErrLed();
185 }
186//
187// if (Flash_Read(flashAddrs.maxAccel, &value)){
188// this->maxTorqueRateMS = value;
189// }else{
190// pulseErrLed();
191// }
192
193
194 uint16_t esval, power;
195 if(Flash_Read(flashAddrs.endstop, &esval)) {
196 fx_ratio_i = esval & 0xff;
197 endstopStrength = (esval >> 8) & 0xff;
198 }
199
200
201 if(Flash_Read(flashAddrs.power, &power)){
203 }
204 uint16_t deg_t;
205 if(Flash_Read(flashAddrs.degrees, &deg_t)){
206 this->degreesOfRotation = deg_t & 0x7fff;
207 this->invertAxis = (deg_t >> 15) & 0x1;
209 }
210
211
212 uint16_t effects;
213 if(Flash_Read(flashAddrs.effects1, &effects)){
216 }else{
218 }
219
220 if(Flash_Read(flashAddrs.effects2, &effects)){
223 }
224
225 uint16_t ratio;
226 if(Flash_Read(flashAddrs.encoderRatio, &ratio)){
227 setGearRatio(ratio & 0xff, (ratio >> 8) & 0xff);
228 }
229
230 uint16_t filterStorage;
231 if (Flash_Read(flashAddrs.speedAccelFilter, &filterStorage))
232 {
233 uint8_t profile = filterStorage & 0xFF;
234 this->filterProfileId = profile;
236 speedFilter.setQ(filterSpeedCst[this->filterProfileId].q / 100.0);
238 accelFilter.setQ(filterAccelCst[this->filterProfileId].q / 100.0);
239 }
240
241 uint16_t pp1;
242 if(Flash_Read(flashAddrs.postprocess1, &pp1)){
243 setExpo((int8_t)(pp1 & 0xff));
244 }
245
246}
247// Saves parameters to flash.
249 //NormalizedAxis::saveFlash();
250 Flash_Write(flashAddrs.config, Axis::encodeConfToInt(this->conf));
251 Flash_Write(flashAddrs.maxSpeed, this->maxSpeedDegS);
252// Flash_Write(flashAddrs.maxAccel, (uint16_t)(this->maxTorqueRateMS));
253
256 Flash_Write(flashAddrs.degrees, (degreesOfRotation & 0x7fff) | (invertAxis << 15));
259 Flash_Write(flashAddrs.encoderRatio, gearRatio.numerator | (gearRatio.denominator << 8));
260
261 // save CF biquad
262 uint16_t filterStorage = (uint16_t)this->filterProfileId & 0xFF;
263 Flash_Write(flashAddrs.speedAccelFilter, filterStorage);
264
265 // Postprocessing
266 Flash_Write(flashAddrs.postprocess1, expoValInt & 0xff);
267
268}
269
270
272 return (uint8_t)this->conf.drvtype;
273}
274
276 if(drv->hasIntegratedEncoder()){
277 return 255;
278 }
279 return (uint8_t)this->conf.enctype;
280}
281
282
283void Axis::setPos(uint16_t val)
284{
285 startForceFadeIn(0.25,0.5);
286 Encoder* enc_p = getEncoder();
287 if(enc_p != nullptr){
288 enc_p->setPos(val);
289 }
290}
291
293 return drv.get();
294}
295
297 if(!drv) return nullptr;
298 return drv->getEncoder();
299}
300
305 if (drv == nullptr){
306 pulseErrLed();
307 return;
308 }
309
310 //if (!drv->motorReady()) return;
311
312 float angle = getEncAngle(getEncoder());
313
314 // Scale encoder value to set rotation range
315 // Update a change of range only when new range is within valid range
316 // if degree change, compute the SpeedScaler, it depends on degreesOfRotation
318 int32_t scaledEnc;
319 std::tie(scaledEnc,std::ignore) = scaleEncValue(angle, nextDegreesOfRotation);
320 if(abs(scaledEnc) < 0x7fff){
322 }
323
324 }
325
326
327 // scaledEnc now gets inverted if necessary in updateMetrics
328 int32_t scaledEnc;
329 std::tie(scaledEnc,std::ignore) = scaleEncValue(angle, degreesOfRotation);
330
331 if (abs(scaledEnc) > 0xffff && drv->motorReady()){
332 // We are way off. Shut down
333 drv->stopMotor();
334 pulseErrLed();
335 if(!outOfBounds){
336 outOfBounds = true;
338 }
339
340 }else if(abs(scaledEnc) <= 0x7fff) {
341 outOfBounds = false;
342 //ErrorHandler::clearError(outOfBoundsError);
343 }
344
345 // On first change to ready start a fade
346 if(motorWasNotReady && drv->motorReady()){
347 motorWasNotReady = false;
348 startForceFadeIn(0, 1.0);
349 }
350
351
352 this->updateMetrics(angle);
353
354}
355
356void Axis::errorCallback(const Error &error, bool cleared){
357 if(cleared && error == this->outOfBoundsError){
358 drv->startMotor();
359 outOfBounds = false;
360 }
361}
362
363
365 // totalTorque = effectTorque + endstopTorque
366 int32_t totalTorque;
367 bool torqueChanged = updateTorque(&totalTorque);
368 if (torqueChanged && drv->motorReady()){
369 // Send to motor driver
370 drv->turn(totalTorque);
371 }
372}
373
374void Axis::setPower(uint16_t power)
375{
376 this->power = power;
378#ifdef TMC4671DRIVER
379 // Update hardware limits for TMC for safety
380 TMC4671 *drv = dynamic_cast<TMC4671 *>(this->drv.get());
381 if (drv != nullptr)
382 {
383 //tmclimits.pid_uq_ud = power;
384 //tmclimits.pid_torque_flux = power;
385 drv->setTorqueLimit(power);
386 }
387#endif
388}
389
390
395{
396 if (!drv_chooser.isValidClassId(drvtype))
397 {
398 return;
399 }
401 this->drv.reset(drv_chooser.Create((uint16_t)drvtype));
402 if (drv == nullptr)
403 {
405 return;
406 }
407 this->conf.drvtype = drvtype;
408
409 // Pass encoder to driver again
410 if(!this->drv->hasIntegratedEncoder()){
411 this->drv->setEncoder(this->enc);
412 }
413#ifdef TMC4671DRIVER
414 if (dynamic_cast<TMC4671 *>(drv.get()))
415 {
416 setupTMC4671();
417 }
418#endif
419 if (!tud_connected())
420 {
421 control->usb_disabled = false;
422 this->usbSuspend();
423 }
424 else
425 {
426 drv->startMotor();
427 }
429}
430
431#ifdef TMC4671DRIVER
432// Special tmc setup methods
434{
435 TMC4671 *drv = static_cast<TMC4671 *>(this->drv.get());
436// drv->setAxis(axis);
437 drv->setExternalEncoderAllowed(true);
438 drv->restoreFlash();
439 tmclimits.pid_torque_flux = getPower();
440 drv->setLimits(tmclimits);
441 //drv->setBiquadTorque(TMC4671Biquad(tmcbq_500hz_07q_25k));
442
443
444 // Enable driver
445
446 drv->setMotionMode(MotionMode::torque);
447 drv->Start(); // Start thread
448}
449#endif
450
451
456{
457 if (enc_chooser.isValidClassId(enctype) && !drv->hasIntegratedEncoder())
458 {
459
460 this->conf.enctype = (enctype);
461 this->enc = std::shared_ptr<Encoder>(enc_chooser.Create(enctype)); // Make new encoder
462 if(drv && !drv->hasIntegratedEncoder())
463 this->drv->setEncoder(this->enc);
464 }else{
465 this->conf.enctype = 0; // None encoder
466 }
467
468 float angle = getEncAngle(this->getEncoder());
469 //int32_t scaledEnc = scaleEncValue(angle, degreesOfRotation);
470 // reset metrics
471 this->resetMetrics(angle);
472
473}
474
479void Axis::setGearRatio(uint8_t numerator,uint8_t denominator){
480 this->gearRatio.denominator = denominator;
481 this->gearRatio.numerator = numerator;
482 this->gearRatio.gearRatio = ((float)numerator+1.0)/((float)denominator+1.0);
483}
484
489
490std::pair<int32_t,float> Axis::scaleEncValue(float angle, uint16_t degrees){
491 if (degrees == 0){
492 return std::make_pair<int32_t,float>(0x7fff,0.0);
493 }
494
495 int32_t val = (0xffff / (float)degrees) * angle;
496 float val_f = (2.0 / (float)degrees) * angle;
497
498 return std::make_pair(val,val_f);
499}
500
505 if(enc != nullptr){
506 float pos = 360.0 * enc->getPos_f() * gearRatio.gearRatio;
507 if (isInverted()){
508 pos= -pos;
509 }
510 return pos;
511 }
512 else{
513 return 0;
514 }
515}
516
520void Axis::emergencyStop(bool reset){
521 drv->turn(0); // Send 0 torque first
522 if(reset){
524 }
525 drv->emergencyStop(reset);
526 control->emergency = !reset;
527}
528
533 if (drv != nullptr){
534 drv->turn(0);
535 drv->stopMotor();
536 }
537}
538
544 if (drv != nullptr){
545 drv->startMotor();
546 }
547}
548
549
550
552 return &metric.current;
553}
554
559 return clip(metric.current.pos,-0x7fff,0x7fff);
560}
561
568
569/*
570 * Set the strength of the spring effect if FFB is disabled
571 */
574 idlespringclip = clip<int32_t,int32_t>((int32_t)spring*35,0,10000);
575 idlespringscale = 0.5f + ((float)spring * 0.01f);
576}
577
581void Axis::setFxStrengthAndFilter(uint8_t val,uint8_t& valToSet, Biquad& filter){
582 if(valToSet == 0 && val != 0)
583 filter.calcBiquad();
584
585 valToSet = val;
586}
587
594
595 if(!ffb_on){
597 }
598
599 // Always active damper
600 if(damperIntensity != 0){
601 float speedFiltered = (metric.current.speed) * (float)damperIntensity * AXIS_DAMPER_RATIO;
603 }
604
605 // Always active inertia
606 if(inertiaIntensity != 0){
607 float accelFiltered = metric.current.accel * (float)inertiaIntensity * AXIS_INERTIA_RATIO;
609 }
610
611 // Always active friction. Based on effectsCalculator implementation
612 if(frictionIntensity != 0){
613 float speed = metric.current.speed * INTERNAL_SCALER_FRICTION;
614 float speedRampupCeil = 4096;
615 float rampupFactor = 1.0;
616 if (fabs (speed) < speedRampupCeil) { // if speed in the range to rampup we apply a sine curve
617 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
618 rampupFactor = ( 1 + sin(phaseRad ) ) / 2; // sin value is -1..1 range, we translate it to 0..2 and we scale it by 2
619 }
620 int8_t sign = speed >= 0 ? 1 : -1;
621 float force = (float)frictionIntensity * rampupFactor * sign * INTERNAL_AXIS_FRICTION_SCALER * 32;
623 }
624
625}
626
630void Axis::setFxRatio(uint8_t val) {
631 fx_ratio_i = val;
633}
634
638void Axis::resetMetrics(float new_pos= 0) { // pos is degrees
639 metric.current = metric_t();
640 metric.current.posDegrees = new_pos;
641 std::tie(metric.current.pos,metric.current.pos_f) = scaleEncValue(new_pos, degreesOfRotation);
642 metric.previous = metric_t();
643 // Reset filters
644 speedFilter.calcBiquad();
645 accelFilter.calcBiquad();
646}
647
651void Axis::updateMetrics(float new_pos) { // pos is degrees
652 // store old value for next metric's computing
653 metric.previous = metric.current;
654
655 metric.current.posDegrees = new_pos;
656 std::tie(metric.current.pos,metric.current.pos_f) = scaleEncValue(new_pos, degreesOfRotation);
657
658
659 // compute speed and accel from raw instant speed normalized
660 float currentSpeed = (new_pos - metric.previous.posDegrees) * 1000.0; // deg/s
661 metric.current.speed = speedFilter.process(currentSpeed);
662 metric.current.accel = accelFilter.process((currentSpeed - _lastSpeed))* 1000.0; // deg/s/s
663 _lastSpeed = currentSpeed;
664
665}
666
667
668
669uint16_t Axis::getPower(){
670 return power;
671}
672
677 float torquef = (float)torque / (float)0x7fff; // This down and upscaling may introduce float artifacts. Do this before scaling down.
678 if(torquef < 0){
679 return -powf(-torquef,expo) * 0x7fff;
680 }else{
681 return powf(torquef,expo) * 0x7fff;
682 }
683}
684
686 effect_margin_scaler = ((float)fx_ratio_i/255.0);
687 torqueScaler = ((float)power / (float)0x7fff);
688}
689
691 return torqueScaler;
692}
693
694
695int32_t Axis::getTorque() { return metric.current.torque; }
696
698 return invertAxis;
699}
700
705 int8_t clipdir = cliptest<int32_t,int32_t>(metric.current.pos, -0x7fff, 0x7fff);
706 if(clipdir == 0){
707 return 0;
708 }
709 float addtorque = clipdir*metric.current.posDegrees - (float)this->degreesOfRotation/2.0; // degress of rotation counts total range so multiply by 2
710 addtorque *= (float)endstopStrength * endstopGain; // Apply endstop gain for stiffness.
711 addtorque *= -clipdir;
712
713 return clip<int32_t,int32_t>(addtorque,-0x7fff,0x7fff);
714}
715
719
723
724bool Axis::updateTorque(int32_t* totalTorque) {
725
726 if(abs(effectTorque) >= 0x7fff){
727 pulseClipLed();
728 }
729
730 // Scale effect torque
731 int32_t torque = effectTorque; // Game effects
732 if(expo != 1){
734 }
736 torque += axisEffectTorque; // Independent effects
738 torque *= torqueScaler; // Scale to power
739
740
741 // TODO speed and accel limiters
742 if(maxSpeedDegS > 0){
743
744 float torqueSign = torque > 0 ? 1 : -1; // Used to prevent metrics against the force to go into the limiter
745 // Speed. Mostly tuned...
746 //spdlimiterAvg.addValue(metric.current.speed);
747 float speedreducer = (float)((metric.current.speed*torqueSign) - (float)maxSpeedDegS) * ((float)0x7FFF / maxSpeedDegS);
749
750 // Accel limit. Not really useful. Maybe replace with torque slew rate limit?
751// float accreducer = (float)((metric.current.accel*torqueSign) - (float)maxAccelDegSS) * getAccelScalerNormalized();
752// acclimitreducerI = clip<float,int32_t>( acclimitreducerI + ((accreducer * 0.02) * torqueScaler),0,power);
753
754
755 // Only reduce torque. Don't invert it to prevent oscillation
756 float torqueReduction = speedreducer * speedLimiterP + spdlimitreducerI;// accreducer * 0.025 + acclimitreducerI
757 if(torque > 0){
758 torqueReduction = clip<float,int32_t>(torqueReduction,0,torque);
759 }else{
760 torqueReduction = clip<float,int32_t>(-torqueReduction,torque,0);
761 }
762
763 torque -= torqueReduction;
764 }
765 // Torque slew rate limiter
766 if(maxTorqueRateMS > 0){
767 torque = clip<int32_t,int32_t>(torque, metric.previous.torque - maxTorqueRateMS,metric.previous.torque + maxTorqueRateMS);
768 }
769// if(torque - metric.previous.torque)
770 if(outOfBounds){
771 torque = 0;
772 }
773
774 // Fade in
775 if(forceFadeCurMult < 1){
777 forceFadeCurMult += forceFadeTime / this->filter_f; // Fade time
778 }
779
780 // Torque calculated. Now sending to driver
782 metric.current.torque = torque;
784
785 bool torqueChanged = metric.current.torque != metric.previous.torque;
786
787 if (abs(torque) == power){
788 pulseClipLed();
789 }
790
791 *totalTorque = torque;
792 return (torqueChanged);
793}
794
798void Axis::startForceFadeIn(float start,float fadeTime){
799 this->forceFadeTime = fadeTime;
800 this->forceFadeCurMult = clip<float>(start, 0, 1);
801}
802
803
808
809 degrees &= 0x7fff;
810 if(degrees == 0){
812 }else{
815 }
816}
817
818
819void Axis::setExpo(int val){
820 val = clip(val, -127, 127);
821 expoValInt = val;
822 if(val == 0){
823 expo = 1; // Explicitly force expo off
824 return;
825 }
826 float valF = abs((float)val / expoScaler);
827 if(val < 0){
828 expo = 1.0f/(1.0f+valF);
829 }else{
830 expo = 1+valF;
831 }
832}
833
834CommandStatus Axis::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
835
836 switch(static_cast<Axis_commands>(cmd.cmdId)){
837
839 if (cmd.type == CMDtype::get)
840 {
841 replies.emplace_back(this->power);
842 }
843 else if (cmd.type == CMDtype::set)
844 {
845 setPower(cmd.val);
846 }
847 break;
848
851// if (cmd.type == CMDtype::get)
852// {
853// replies.emplace_back(degreesOfRotation);
854// }
855// else if (cmd.type == CMDtype::set)
856// {
857// setDegrees(cmd.val);
858// }
859 break;
860
862 handleGetSet(cmd, replies, this->endstopStrength);
863 break;
864
866 this->setPos(0);
867 break;
868
870 if (cmd.type == CMDtype::get)
871 {
872 replies.emplace_back(invertAxis ? 1 : 0);
873 }
874 else if (cmd.type == CMDtype::set)
875 {
876 invertAxis = cmd.val >= 1 ? true : false;
877 resetMetrics(-metric.current.posDegrees);
878 }
879 break;
880
882 if (cmd.type == CMDtype::get)
883 {
884 replies.emplace_back(idlespringstrength);
885 }
886 else if (cmd.type == CMDtype::set)
887 {
889 }
890 break;
891
893 if (cmd.type == CMDtype::get)
894 {
895 replies.emplace_back(damperIntensity);
896 }
897 else if (cmd.type == CMDtype::set)
898 {
899 setFxStrengthAndFilter(cmd.val,this->damperIntensity,this->damperFilter);
900 }
901 break;
902
904 if (cmd.type == CMDtype::get)
905 {
906 replies.emplace_back(frictionIntensity);
907 }
908 else if (cmd.type == CMDtype::set)
909 {
910 setFxStrengthAndFilter(cmd.val,this->frictionIntensity,this->frictionFilter);
911 }
912 break;
913
915 if (cmd.type == CMDtype::get)
916 {
917 replies.emplace_back(inertiaIntensity);
918 }
919 else if (cmd.type == CMDtype::set)
920 {
921 setFxStrengthAndFilter(cmd.val,this->inertiaIntensity,this->inertiaFilter);
922 }
923 break;
924
926 if(cmd.type == CMDtype::info){
927 enc_chooser.replyAvailableClasses(replies,this->getEncType());
928 }else if(cmd.type == CMDtype::get){
929 replies.emplace_back(this->getEncType());
930 }else if(cmd.type == CMDtype::set){
931 this->setEncType(cmd.val);
932 }
933 break;
934
936 if(cmd.type == CMDtype::info){
937 drv_chooser.replyAvailableClasses(replies,this->getDrvType());
938 }else if(cmd.type == CMDtype::get){
939 replies.emplace_back(this->getDrvType());
940 }else if(cmd.type == CMDtype::set){
941 this->setDrvType(cmd.val);
942 }
943 break;
944
946 if (cmd.type == CMDtype::get && getEncoder() != nullptr)
947 {
948 int32_t pos = getEncoder()->getPos();
949 replies.emplace_back(isInverted() ? -pos : pos);
950 }
951 else if (cmd.type == CMDtype::set && getEncoder() != nullptr)
952 {
953 getEncoder()->setPos(isInverted() ? -cmd.val : cmd.val);
954 }
955 else
956 {
957 return CommandStatus::ERR;
958 }
959 break;
960
962 handleGetSet(cmd, replies, this->maxSpeedDegS);
963 break;
964
966 handleGetSet(cmd, replies, this->maxTorqueRateMS);
967 break;
968
970 if(cmd.type == CMDtype::get){
971 replies.emplace_back(this->fx_ratio_i);
972 }else if(cmd.type == CMDtype::set){
973 setFxRatio(cmd.val);
974 }
975 break;
976
978 replies.emplace_back(this->metric.current.pos);
979 break;
981 replies.emplace_back(this->metric.current.torque);
982 break;
984 replies.emplace_back(this->metric.current.speed);
985 break;
987 replies.emplace_back(this->metric.current.accel);
988 break;
989
991 if(cmd.type == CMDtype::get){
992 replies.emplace_back(gearRatio.numerator+1,gearRatio.denominator+1);
993 }else if(cmd.type == CMDtype::setat){
994 setGearRatio(cmd.val-1,cmd.adr-1);
995 }
996 break;
997
999 if (cmd.type == CMDtype::get)
1000 {
1001 replies.emplace_back(this->filterProfileId);
1002 }
1003 else if (cmd.type == CMDtype::set)
1004 {
1005 uint32_t value = clip<uint32_t, uint32_t>(cmd.val, 0, filterSpeedCst.size()-1);
1006 this->filterProfileId = value;
1008 speedFilter.setQ(filterSpeedCst[this->filterProfileId].q / 100.0);
1010 accelFilter.setQ(filterAccelCst[this->filterProfileId].q / 100.0);
1011 }
1012 break;
1014 if (cmd.type == CMDtype::get)
1015 {
1016 replies.emplace_back(this->filterSpeedCst[this->filterProfileId].freq,this->filterSpeedCst[this->filterProfileId].q);
1017 }
1018 break;
1019
1021 if (cmd.type == CMDtype::get)
1022 {
1023 replies.emplace_back(this->filterAccelCst[this->filterProfileId].freq,this->filterAccelCst[this->filterProfileId].q);
1024 }
1025 break;
1026
1027 case Axis_commands::cpr:
1028 if (cmd.type == CMDtype::get)
1029 {
1030 uint32_t cpr = 0;
1031 if(this->getEncoder() != nullptr){
1032 cpr = this->getEncoder()->getCpr();
1033 }
1034//#ifdef TMC4671DRIVER // CPR should be consistent with position. Maybe change TMC to prescale to encoder count or correct readout in UI
1035// TMC4671 *tmcdrv = dynamic_cast<TMC4671 *>(this->drv.get()); // Special case for TMC. Get the actual encoder resolution
1036// if (tmcdrv && tmcdrv->hasIntegratedEncoder())
1037// {
1038// cpr = tmcdrv->getEncCpr();
1039// }
1040//#endif
1041 replies.emplace_back(cpr);
1042 }else{
1043 return CommandStatus::ERR;
1044 }
1045 break;
1046
1048 handleGetSetFunc(cmd, replies, expoValInt, &Axis::setExpo, this); // need to also provide the expoScaler constant
1049 break;
1050
1052 handleGetSet(cmd, replies, expoScaler); // need to also provide the expoScaler constant
1053 break;
1054
1055 default:
1057 }
1058 return CommandStatus::OK;
1059}
1060
1061
1062
1063/*
1064 * Helper functions for encoding and decoding flash variables
1065 */
1067{
1068 // 0-6 enc, 7-12 Mot
1070 conf.enctype = ((val)&0x3f);
1071 conf.drvtype = ((val >> 6) & 0x3f);
1072 return conf;
1073}
1074
1076{
1077 uint16_t val = (uint8_t)conf.enctype & 0x3f;
1078 val |= ((uint8_t)conf.drvtype & 0x3f) << 6;
1079 return val;
1080}
Axis_commands
Definition Axis.h:93
@ reductionScaler
Definition Axis.h:95
@ curtorque
Definition Axis.h:95
@ axisinertia
Definition Axis.h:96
@ filterSpeed
Definition Axis.h:96
@ axisdamper
Definition Axis.h:94
@ idlespring
Definition Axis.h:94
@ axisfriction
Definition Axis.h:96
@ filterAccel
Definition Axis.h:96
@ filterProfileId
Definition Axis.h:96
@ exposcale
Definition Axis.h:97
@ maxtorquerate
Definition Axis.h:95
constexpr class_entry< B > add_class(std::optional< uint16_t > selectionId=std::nullopt)
CommandStatus
float forceFadeTime
Definition Axis.h:200
float effect_margin_scaler
Definition Axis.h:244
float getTorqueScaler()
Definition Axis.cpp:690
void errorCallback(const Error &error, bool cleared) override
Definition Axis.cpp:356
const std::array< biquad_constant_t, 4 > filterAccelCst
Definition Axis.h:257
float getEncAngle(Encoder *enc)
Definition Axis.cpp:504
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:798
int32_t getTorque()
Definition Axis.cpp:695
axis_metric_t metric
Definition Axis.h:237
void setEffectTorque(int32_t torque)
Definition Axis.cpp:716
GearRatio_t gearRatio
Definition Axis.h:282
bool isInverted()
Definition Axis.cpp:697
uint16_t nextDegreesOfRotation
Definition Axis.h:215
Axis(char axis, volatile Control_t *control)
Definition Axis.cpp:89
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:581
int32_t updateIdleSpringForce()
Definition Axis.cpp:565
void setDegrees(uint16_t degrees)
Definition Axis.cpp:807
void calculateAxisEffects(bool ffb_on)
Definition Axis.cpp:592
bool updateTorque(int32_t *totalTorque)
Definition Axis.cpp:724
uint8_t damperIntensity
Definition Axis.h:264
uint8_t filterProfileId
Definition Axis.h:261
uint16_t getPower()
Definition Axis.cpp:669
uint8_t getEncType()
Definition Axis.cpp:275
volatile Control_t * control
Definition Axis.h:185
std::pair< int32_t, float > scaleEncValue(float angle, uint16_t degrees)
Definition Axis.cpp:490
void setIdleSpringStrength(uint8_t spring)
Definition Axis.cpp:572
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:532
const Error outOfBoundsError
Definition Axis.h:198
Biquad speedFilter
Definition Axis.h:269
void setPower(uint16_t power)
Definition Axis.cpp:374
MotorDriver * getDriver()
Definition Axis.cpp:292
virtual ~Axis()
Definition Axis.cpp:124
static const std::vector< class_entry< MotorDriver > > axis1_drivers
Definition Axis.h:34
AxisConfig conf
Definition Axis.h:188
static const std::vector< class_entry< MotorDriver > > axis2_drivers
Definition Axis.h:61
void updateMetrics(float new_pos)
Definition Axis.cpp:651
static uint16_t encodeConfToInt(AxisConfig conf)
Definition Axis.cpp:1075
void resetMetrics(float new_pos)
Definition Axis.cpp:638
int16_t updateEndstop()
Definition Axis.cpp:704
const int32_t intFxClip
Definition Axis.h:263
Biquad damperFilter
Definition Axis.h:271
void registerCommands()
Definition Axis.cpp:134
Encoder * getEncoder()
Definition Axis.cpp:296
void setupTMC4671()
Definition Axis.cpp:433
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies)
Definition Axis.cpp:834
void setFxRatio(uint8_t val)
Definition Axis.cpp:630
ClassChooser< Encoder > enc_chooser
Definition Axis.h:152
static ClassIdentifier info
Definition Axis.h:26
const float endstopGain
Definition Axis.h:247
void setEncType(uint8_t enctype)
Definition Axis.cpp:455
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:551
void setPos(uint16_t val)
Definition Axis.cpp:283
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:676
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:558
void restoreFlash() override
Definition Axis.cpp:168
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:479
uint16_t degreesOfRotation
Definition Axis.h:213
void setExpo(int val)
Definition Axis.cpp:819
int32_t effectTorque
Definition Axis.h:239
int16_t idlespringclip
Definition Axis.h:251
void updateDriveTorque()
Definition Axis.cpp:364
void setDrvType(uint8_t drvtype)
Definition Axis.cpp:394
void saveFlash() override
Definition Axis.cpp:248
float spdlimitreducerI
Definition Axis.h:224
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition Axis.cpp:129
void usbResume()
Definition Axis.cpp:542
void emergencyStop(bool reset)
Definition Axis.cpp:520
const float AXIS_DAMPER_RATIO
Definition Axis.h:181
static AxisConfig decodeConfFromInt(uint16_t val)
Definition Axis.cpp:1066
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:685
void prepareForUpdate()
Definition Axis.cpp:304
uint8_t getDrvType()
Definition Axis.cpp:271
float _lastSpeed
Definition Axis.h:238
Biquad inertiaFilter
Definition Axis.h:273
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)
CommandHandler(const char *clsname, uint16_t clsid, uint8_t instance=0)
virtual int32_t getPos()
Definition Encoder.cpp:44
virtual uint32_t getCpr()
Definition Encoder.cpp:36
virtual void setPos(int32_t pos)
Definition Encoder.cpp:78
static void addError(const Error &error)
T clip(T v, C l, C h)
Definition cppmain.h:58
int8_t cliptest(T v, C l, C h)
Definition cppmain.h:46
bool Flash_Write(uint16_t adr, uint16_t dat)
bool Flash_Read(uint16_t adr, uint16_t *buf, bool checkempty=true)
void pulseClipLed()
void pulseErrLed()
bool tud_connected(void)
Definition usbd.c:411