Skip to content

Commit cef7695

Browse files
committed
fix: resolve boot-up deadlocks and refactor TMC state machine to non-blocking
Refactored Axis::setDrvType to instantiate driver outside critical section, preventing FreeRTOS crashes. Converted TMC4671 state machine to an asynchronous, non-blocking model. Updated existing handlers (WaitPower, Running, FullCalibration, PID AutoTune) to use a tick-based approach. Refactored header constants to use constexpr and UPPERCASE naming. Integrated updated documentation.
1 parent 56dce4b commit cef7695

7 files changed

Lines changed: 412 additions & 322 deletions

File tree

Firmware/FFBoard/Inc/Axis.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ struct AxisFlashAddresses
6060
uint16_t config = ADR_AXIS1_CONFIG;
6161
uint16_t maxSpeed = ADR_AXIS1_MAX_SPEED;
6262
uint16_t maxAccel = ADR_AXIS1_MAX_ACCEL;
63-
uint16_t maxSlewRateDrv = ADR_AXIS1_MAX_SLEWRATE_DRV;
6463

6564
uint16_t endstop = ADR_AXIS1_ENDSTOP;
6665
uint16_t power = ADR_AXIS1_POWER;
@@ -123,8 +122,6 @@ enum class Axis_commands : uint32_t{
123122
fxratio,reductionScaler,
124123
filterSpeed, filterAccel, filterProfileId,cpr,axisfriction,axisinertia,
125124
maxspeed,slewrate,
126-
calibrate_maxSlewRateDrv,
127-
maxSlewRateDrv,
128125
expo,exposcale,
129126
equalizer,eqb1,eqb2,eqb3,eqb4,eqb5,eqb6,
130127
handsoff, handsoff_speed, handsoff_accel
@@ -477,7 +474,6 @@ class Axis : public PersistentStorage, public CommandHandler, public ErrorHandle
477474
uint16_t nextDegreesOfRotation = degreesOfRotation; //!< Target degrees of rotation.
478475

479476
// Limiters
480-
uint16_t maxSlewRate_Driver = MAX_SLEW_RATE; //!< Maximum slew rate as measured by the driver (in units/ms).
481477
uint16_t maxSpeedDegS = 0; //!< Maximum speed in degrees per second. 0 to disable.
482478
uint32_t maxTorqueRateMS = 0; //!< Maximum torque rate of change per millisecond. 0 to disable.
483479

@@ -507,17 +503,14 @@ class Axis : public PersistentStorage, public CommandHandler, public ErrorHandle
507503
// Axis configuration
508504
bool invertAxis = true; //!< Invert axis direction.
509505
uint8_t endstopStrength = 127; //!< Stiffness of the endstop effect.
510-
const float endstopGain = 25; //!< Overall maximum endstop intensity.
506+
static constexpr float ENDSTOP_GAIN = 25.0f; //!< Overall maximum endstop intensity.
511507

512508
// Idle spring effect
513509
uint8_t idleSpringStrength = 127; //!< Strength of the idle spring.
514510
int16_t idleSpringClip = 0; //!< Maximum force for the idle spring.
515511
float idleSpringScale = 0; //!< Scaler for the idle spring force.
516512
bool motorWasNotReady = true; //!< Flag to detect motor readiness transition.
517513

518-
// Slew rate calibration tracking: true when Axis requested a calibration and
519-
// is waiting for the driver to finish measuring the max slew rate.
520-
bool awaitingSlewCalibration = false;
521514

522515
// Filters
523516
// TODO tune these and check if it is really stable and beneficial to the FFB. index 4 placeholder
@@ -528,7 +521,7 @@ class Axis : public PersistentStorage, public CommandHandler, public ErrorHandle
528521
const biquad_constant_t filterInertiaCst = {20, 20}; //!< Inertia filter constants.
529522
uint8_t filterProfileId = 1; //!< Currently selected filter profile ID.
530523
float filter_f = 1000.0; // 1khz
531-
const int32_t internalFxClip = 20000; //!< Clipping value for internal effects.
524+
static constexpr int32_t INTERNAL_FX_CLIP = 20000; //!< Clipping value for internal effects.
532525

533526
// Internal effects intensity
534527
uint8_t damperIntensity = 30; //!< Intensity of the internal damper effect.

Firmware/FFBoard/Inc/flash_helpers.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ bool Flash_ReadWriteDefault(uint16_t adr,uint16_t *buf,uint16_t def); // returns
3232
void Flash_Dump(std::vector<std::tuple<uint16_t,uint16_t>> *result,bool includeAll = false);
3333
bool Flash_Format();
3434

35-
#ifdef COGGING_TABLE_FLASH_START_ADDRESS
36-
bool Flash_WriteCoggingTable(uint8_t table_idx, int16_t* data);
37-
bool Flash_ReadCoggingTable(uint8_t table_idx, int16_t* data);
38-
#endif
39-
4035
bool OTP_Write(uint16_t adroffset,uint64_t dat);
4136
bool OTP_Read(uint16_t adroffset,uint64_t* dat);
4237

Firmware/FFBoard/Src/Axis.cpp

Lines changed: 31 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Axis::Axis(char axis,volatile Control_t* control) :CommandHandler("axis", CLSID_
117117
{
118118
driverChooser = ClassChooser<MotorDriver>(axis1_drivers);
119119
setInstance(0);
120-
this->flashAddresses = AxisFlashAddresses({ADR_AXIS1_CONFIG, ADR_AXIS1_MAX_SPEED, ADR_AXIS1_MAX_ACCEL, ADR_AXIS1_MAX_SLEWRATE_DRV,
120+
this->flashAddresses = AxisFlashAddresses({ADR_AXIS1_CONFIG, ADR_AXIS1_MAX_SPEED, ADR_AXIS1_MAX_ACCEL,
121121
ADR_AXIS1_ENDSTOP, ADR_AXIS1_POWER, ADR_AXIS1_DEGREES,ADR_AXIS1_EFFECTS1,ADR_AXIS1_EFFECTS2,ADR_AXIS1_ENC_RATIO,
122122
ADR_AXIS1_SPEEDACCEL_FILTER,ADR_AXIS1_POSTPROCESS1,
123123
ADR_AXIS1_EQ1,ADR_AXIS1_EQ2,ADR_AXIS1_EQ3,
@@ -128,7 +128,7 @@ Axis::Axis(char axis,volatile Control_t* control) :CommandHandler("axis", CLSID_
128128
{
129129
driverChooser = ClassChooser<MotorDriver>(axis2_drivers);
130130
setInstance(1);
131-
this->flashAddresses = AxisFlashAddresses({ADR_AXIS2_CONFIG, ADR_AXIS2_MAX_SPEED, ADR_AXIS2_MAX_ACCEL, ADR_AXIS2_MAX_SLEWRATE_DRV,
131+
this->flashAddresses = AxisFlashAddresses({ADR_AXIS2_CONFIG, ADR_AXIS2_MAX_SPEED, ADR_AXIS2_MAX_ACCEL,
132132
ADR_AXIS2_ENDSTOP, ADR_AXIS2_POWER, ADR_AXIS2_DEGREES,ADR_AXIS2_EFFECTS1,ADR_AXIS2_EFFECTS2, ADR_AXIS2_ENC_RATIO,
133133
ADR_AXIS2_SPEEDACCEL_FILTER,ADR_AXIS2_POSTPROCESS1,
134134
ADR_AXIS2_EQ1,ADR_AXIS2_EQ2,ADR_AXIS2_EQ3,
@@ -138,7 +138,7 @@ Axis::Axis(char axis,volatile Control_t* control) :CommandHandler("axis", CLSID_
138138
else if (axis == 'Z')
139139
{
140140
setInstance(2);
141-
this->flashAddresses = AxisFlashAddresses({ADR_AXIS3_CONFIG, ADR_AXIS3_MAX_SPEED, ADR_AXIS3_MAX_ACCEL, ADR_AXIS3_MAX_SLEWRATE_DRV,
141+
this->flashAddresses = AxisFlashAddresses({ADR_AXIS3_CONFIG, ADR_AXIS3_MAX_SPEED, ADR_AXIS3_MAX_ACCEL,
142142
ADR_AXIS3_ENDSTOP, ADR_AXIS3_POWER, ADR_AXIS3_DEGREES,ADR_AXIS3_EFFECTS1,ADR_AXIS3_EFFECTS2,ADR_AXIS3_ENC_RATIO,
143143
ADR_AXIS3_SPEEDACCEL_FILTER,ADR_AXIS3_POSTPROCESS1,
144144
ADR_AXIS3_EQ1,ADR_AXIS3_EQ2,ADR_AXIS3_EQ3,
@@ -207,9 +207,6 @@ void Axis::registerCommands(){
207207
registerCommand("handsoff", Axis_commands::handsoff, "Hands-off enable", CMDFLAG_GET | CMDFLAG_SET);
208208
registerCommand("handsoff_speed", Axis_commands::handsoff_speed, "Hoff speed thrld (deg/s)", CMDFLAG_GET | CMDFLAG_SET);
209209
registerCommand("handsoff_accel", Axis_commands::handsoff_accel, "Hoff accel std dev thrld (float, val/1000)", CMDFLAG_GET | CMDFLAG_SET);
210-
211-
registerCommand("maxSlewRateDrv", Axis_commands::maxSlewRateDrv, "Max driver torque in counts/ms",CMDFLAG_GET);
212-
registerCommand("calibrate_maxSlewRateDrv", Axis_commands::calibrate_maxSlewRateDrv, "Start driver slewRate calib", CMDFLAG_GET);
213210
}
214211

215212
/*
@@ -241,13 +238,6 @@ void Axis::restoreFlash(){
241238
pulseErrLed();
242239
}
243240

244-
// save the max torque for the slew rate
245-
if (Flash_Read(flashAddresses.maxSlewRateDrv, &value)){
246-
this->maxSlewRate_Driver = value;
247-
}else{
248-
pulseErrLed();
249-
}
250-
251241

252242
uint16_t endstopRawValue, power;
253243
if(Flash_Read(flashAddresses.endstop, &endstopRawValue)) {
@@ -332,7 +322,6 @@ void Axis::saveFlash(){
332322
Flash_Write(flashAddresses.config, Axis::encodeConfToInt(this->conf));
333323
Flash_Write(flashAddresses.maxSpeed, this->maxSpeedDegS);
334324
Flash_Write(flashAddresses.maxAccel, (uint16_t)(this->maxTorqueRateMS));
335-
Flash_Write(flashAddresses.maxSlewRateDrv, (uint16_t)(this->maxSlewRate_Driver));
336325

337326
Flash_Write(flashAddresses.endstop, effectRatio | (endstopStrength << 8));
338327
Flash_Write(flashAddresses.power, power);
@@ -446,29 +435,6 @@ void Axis::prepareForUpdate(){
446435
startForceFadeIn(0, 1.0);
447436
}
448437

449-
// Check for pending slew rate calibration result
450-
if(this->awaitingSlewCalibration){
451-
// If driver reports calibration finished, retrieve measured value and persist
452-
if(!drv->isSlewRateCalibrationInProgress()){
453-
// Get value from drv
454-
this->maxSlewRate_Driver = drv->getDrvSlewRate();
455-
456-
// If the driver's max slew rate is lowest thant current max flew rate, cap the value and send the new value to the UI
457-
if (this->maxSlewRate_Driver < this->maxTorqueRateMS) {
458-
this->maxTorqueRateMS = this->maxSlewRate_Driver;
459-
CommandHandler::broadcastCommandReply(CommandReply(this->maxTorqueRateMS), (uint32_t)Axis_commands::slewrate, CMDtype::get);
460-
}
461-
462-
// Broadcast a friendly completion message and the numeric value
463-
CommandHandler::broadcastCommandReply(CommandReply("Slew rate calibration complete",0), (uint32_t)Axis_commands::calibrate_maxSlewRateDrv, CMDtype::get);
464-
CommandHandler::broadcastCommandReply(CommandReply(this->maxSlewRate_Driver), (uint32_t)Axis_commands::maxSlewRateDrv, CMDtype::get);
465-
466-
467-
this->awaitingSlewCalibration = false;
468-
}
469-
}
470-
471-
472438
this->updateMetrics(angle);
473439
this->updateHandsOffState();
474440

@@ -512,33 +478,44 @@ void Axis::setDrvType(uint8_t drvtype)
512478
{
513479
return;
514480
}
515-
cpp_freertos::CriticalSection::Enter();
516-
MotorDriver* drv = driverChooser.Create((uint16_t)drvtype);
517-
this->drv.reset(drv);
518-
if (drv == nullptr)
481+
482+
// Create the driver outside of the critical section to avoid FreeRTOS issues (semaphore take with interrupts disabled)
483+
MotorDriver* drv_new = driverChooser.Create((uint16_t)drvtype);
484+
485+
if (drv_new == nullptr)
519486
{
520-
cpp_freertos::CriticalSection::Exit();
521487
return;
522488
}
489+
490+
// Pre-initialization outside of critical section
491+
if(!drv_new->hasIntegratedEncoder()){
492+
drv_new->setEncoder(this->enc);
493+
}
494+
drv_new->setupDriver();
495+
496+
// Use critical section only for atomic replacement of the driver pointer
497+
MotorDriver* old_drv_ptr = nullptr;
498+
cpp_freertos::CriticalSection::Enter();
499+
old_drv_ptr = this->drv.release(); // Detach the old driver safely
500+
this->drv.reset(drv_new); // Attach the new driver
523501
this->conf.drvtype = drvtype;
524-
this->maxTorqueRateMS = drv->getDrvSlewRate();
502+
cpp_freertos::CriticalSection::Exit();
525503

526-
// Pass encoder to driver again
527-
if(!this->drv->hasIntegratedEncoder()){
528-
this->drv->setEncoder(this->enc);
504+
// Delete the old driver outside of the critical section to avoid blocking destructors or FreeRTOS issues
505+
if (old_drv_ptr != nullptr) {
506+
delete old_drv_ptr;
529507
}
530508

531-
drv->setupDriver();
509+
// Perform SPI communications (start/suspend) outside of the critical section
532510
if (!tud_connected())
533511
{
534512
control->usb_disabled = false;
535513
this->usbSuspend();
536514
}
537515
else
538516
{
539-
drv->startMotor();
517+
drv_new->startMotor();
540518
}
541-
cpp_freertos::CriticalSection::Exit();
542519
}
543520

544521

@@ -694,13 +671,13 @@ void Axis::calculateMechanicalEffects(bool ffb_on){
694671
// Always active damper
695672
if(damperIntensity != 0){
696673
float speedFiltered = (metric.current.speed) * (float)damperIntensity * AXIS_DAMPER_RATIO;
697-
mechanicalEffectTorque -= damperFilter.process(clip<float, int32_t>(speedFiltered, -internalFxClip, internalFxClip));
674+
mechanicalEffectTorque -= damperFilter.process(clip<float, int32_t>(speedFiltered, -INTERNAL_FX_CLIP, INTERNAL_FX_CLIP));
698675
}
699676

700677
// Always active inertia
701678
if(inertiaIntensity != 0){
702679
float accelFiltered = metric.current.accel * (float)inertiaIntensity * AXIS_INERTIA_RATIO;
703-
mechanicalEffectTorque -= inertiaFilter.process(clip<float, int32_t>(accelFiltered, -internalFxClip, internalFxClip));
680+
mechanicalEffectTorque -= inertiaFilter.process(clip<float, int32_t>(accelFiltered, -INTERNAL_FX_CLIP, INTERNAL_FX_CLIP));
704681
}
705682

706683
// Always active friction. Based on effectsCalculator implementation
@@ -719,7 +696,7 @@ void Axis::calculateMechanicalEffects(bool ffb_on){
719696
}
720697
int8_t sign = speed >= 0 ? 1 : -1;
721698
float force = (float)frictionIntensity * rampupFactor * sign * INTERNAL_AXIS_FRICTION_SCALER * 32;
722-
mechanicalEffectTorque -= frictionFilter.process(clip<float, int32_t>(force, -internalFxClip, internalFxClip));
699+
mechanicalEffectTorque -= frictionFilter.process(clip<float, int32_t>(force, -INTERNAL_FX_CLIP, INTERNAL_FX_CLIP));
723700
}
724701

725702
}
@@ -828,7 +805,7 @@ int32_t Axis::calculateEndstopTorque(){
828805
return 0;
829806
}
830807
float endstopTorque = clipDirection*metric.current.posDegrees - (float)this->degreesOfRotation/2.0; // degress of rotation counts total range so multiply by 2
831-
endstopTorque *= (float)endstopStrength * endstopGain; // Apply endstop gain for stiffness.
808+
endstopTorque *= (float)endstopStrength * ENDSTOP_GAIN; // Apply endstop gain for stiffness.
832809
endstopTorque *= -clipDirection;
833810

834811
return clip<int32_t,int32_t>(endstopTorque,-0x7fff,0x7fff);
@@ -1155,35 +1132,10 @@ CommandStatus Axis::command(const ParsedCommand& cmd,std::vector<CommandReply>&
11551132
case Axis_commands::slewrate:
11561133
{
11571134
if(cmd.type == CMDtype::get){
1158-
// If driver has a more restrictive calibrated value, update the axis limit
1159-
if(maxSlewRate_Driver < this->maxTorqueRateMS) {
1160-
this->maxTorqueRateMS = maxSlewRate_Driver;
1161-
}
11621135
replies.emplace_back(this->maxTorqueRateMS);
11631136
}else if(cmd.type == CMDtype::set){
1164-
this->maxTorqueRateMS = clip<uint32_t,uint32_t>(cmd.val, 0, maxSlewRate_Driver);
1165-
}
1166-
}
1167-
break;
1168-
1169-
case Axis_commands::calibrate_maxSlewRateDrv:
1170-
{
1171-
if(cmd.type == CMDtype::get){
1172-
// Start calibration on driver and set awaiting flag if start is OK
1173-
if (drv->startSlewRateCalibration()) {
1174-
this->awaitingSlewCalibration = true;
1175-
} else {
1176-
// Inform user that calibration can't started
1177-
CommandHandler::broadcastCommandReply(CommandReply("Slew rate calibration unsupported",1), (uint32_t)Axis_commands::calibrate_maxSlewRateDrv, CMDtype::get);
1178-
}
1179-
replies.emplace_back(1); // ack
1137+
this->maxTorqueRateMS = cmd.val;
11801138
}
1181-
break;
1182-
}
1183-
1184-
case Axis_commands::maxSlewRateDrv:
1185-
if (cmd.type == CMDtype::get) {
1186-
replies.emplace_back(maxSlewRate_Driver);
11871139
}
11881140
break;
11891141

@@ -1248,14 +1200,6 @@ CommandStatus Axis::command(const ParsedCommand& cmd,std::vector<CommandReply>&
12481200
if(this->getEncoder() != nullptr){
12491201
cpr = this->getEncoder()->getCpr();
12501202
}
1251-
// TODO: For TMC4671 drivers, CPR reporting might be inconsistent. Investigate if a prescale is needed or if the UI should handle the readout correction.
1252-
//#ifdef TMC4671DRIVER // CPR should be consistent with position. Maybe change TMC to prescale to encoder count or correct readout in UI
1253-
// TMC4671 *tmcdrv = dynamic_cast<TMC4671 *>(this->drv.get()); // Special case for TMC. Get the actual encoder resolution
1254-
// if (tmcdrv && tmcdrv->hasIntegratedEncoder())
1255-
// {
1256-
// cpr = tmcdrv->getEncCpr();
1257-
// }
1258-
//#endif
12591203
replies.emplace_back(cpr);
12601204
}else{
12611205
return CommandStatus::ERR;

Firmware/FFBoard/Src/flash_helpers.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
* flash_helpers.cpp
33
*
44
* Created on: 31.01.2020
5-
* Author: Yannick
5+
* Author: Yannick, Vincent
66
*/
77
#include "flash_helpers.h"
88
#include "eeprom_addresses.h"
99
#include <vector>
1010
#include "mutex.hpp"
1111
#include <span>
12+
#include <cstring>
1213

1314
cpp_freertos::MutexStandard flashMutex;
1415
// Flash helpers
@@ -152,7 +153,7 @@ bool I2C_EEPROM_Write16(uint16_t devAdr,uint16_t adr, uint16_t dat){
152153
/**
153154
* Helper function to read 16b data from i2c eeproms with different block sizes
154155
*/
155-
bool I2C_EEPROM_Read16(uint16_t devAdr,uint16_t adr,uint16_t *buf, bool checkempty){
156+
bool I2C_EEPROM_Read16(uint16_t devAdr,uint16_t adr, uint16_t *buf, bool checkempty){
156157
uint32_t adrAbs = (adr * sizeof(*buf)/I2C_EEPROM_DATA_SIZE);
157158
assert(adrAbs < I2C_EEPROM_SIZE);
158159
while(!i2cport_int.isDeviceReady(&i2cdeveeprom, devAdr, 100, I2C_EEPROM_TIMEOUT,false)){
@@ -242,7 +243,9 @@ __weak bool Flash_Format(){
242243
__weak bool Flash_Init(){
243244
return true;
244245
}
246+
245247
#endif
248+
246249
/*
247250
* Dumps all set variables from flash to a vector
248251
* does by default not include certain calibration constants that could break something if imported on a different board

0 commit comments

Comments
 (0)