HardFOC Vortex V1 HAL
Unified HAL API for the HardFOC Vortex motor-control platform (ESP32-C6, TMC9660)
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Static Public Attributes | Private Member Functions | Private Attributes | List of all members
MotorController Class Reference

Singleton managing multiple TMC9660 motor controllers on Vortex V1. More...

#include <MotorController.h>

Collaboration diagram for MotorController:
[legend]

Public Member Functions

bool EnsureInitialized () noexcept
 Initialise all registered motor controllers if not already done.
 
bool IsInitialized () const noexcept
 Check whether the motor controller subsystem has been initialised.
 
bool Deinitialize () noexcept
 Shut down all motor controller devices and release resources.
 
MotorError GetLastError () const noexcept
 Get the most recent error code.
 
MotorError GetSystemDiagnostics (MotorSystemDiagnostics &diagnostics) const noexcept
 Fill a diagnostics snapshot with current system state.
 
MotorError ApplyOnboardTmc9660HostSpiGateMode (Tmc9660HostSpiGateMode mode) noexcept
 Drive the onboard TMC9660 host SPI mux (PCAL_TMC_SPI_COMM_EN) on Vortex.
 
std::optional< tmc9660::CommMode > TryGetOnboardCommMode () const noexcept
 Physical comm mode for the onboard handler (slot 0), if present.
 
MotorError SetTmclSpiTiming (const tmc9660::TmclSpiTiming &timing, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Set the SPI TMCL handshake timing for one TMC9660.
 
MotorError GetTmclSpiTiming (tmc9660::TmclSpiTiming &timing, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) const noexcept
 SPI TMCL timing stored for a slot (the driver defaults unless changed).
 
MotorError GetTmclSpiStats (tmc9660::TmclSpiStats &stats, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 SPI TMCL transport counters (frames, NOT_READY re-sends, stale replies, failures).
 
MotorError ResetTmclSpiStats (uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Reset the SPI TMCL transport counters.
 
MotorError ReadParameters (std::span< MotorParameterAccess > items, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Read several axis parameters in pipelined batches (GAP).
 
MotorError WriteParameters (std::span< MotorParameterAccess > items, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Write several axis parameters in pipelined batches (SAP); per-element status in items.
 
MotorError ReadTelemetry (MotorTelemetrySet set, MotorTelemetrySnapshot &snapshot, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Refresh a caller-owned telemetry snapshot from one batch.
 
MotorError ReadDriveDiagnostics (MotorDriveDiagnostics &out, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Read the four status words (289/299/300/301) in one batch and decode them.
 
MotorError ClearLatchedFaults (uint32_t general_errors, uint32_t gate_driver_errors, uint32_t adc_status, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Clear latched (write-1-to-clear) bits of the general error, gate-driver and ADC words.
 
MotorError ApplyParameterImage (std::span< const MotorParameterWrite > image, const MotorApplyOptions &options, MotorApplyResult &result, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Apply a parameter image (MOTOR_TYPE first), optionally only where the chip differs and with read-back verification. The TMC9660's own NVM is not used (design §4.7).
 
MotorError GetAccessStats (MotorAccessStats &stats, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) const noexcept
 TMCL access accounting of one device (batches, accesses, failures, batch latency).
 
void ResetAccessStats (uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Zero the access accounting of one device.
 
Tmc9660Handler * handler (uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Get access to Tmc9660Handler by device index.
 
template<typename Func >
auto visitDriver (Func &&func, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Visit the underlying typed TMC9660 driver by device index.
 
bool setMotorMechanics (const MotorMechanics &mech, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Set the mechanical mapping (gearbox + direction) for a device.
 
MotorMechanics getMotorMechanics (uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) const noexcept
 Read back the currently configured mechanical mapping.
 
bool setMotorVelocity (double value, ::tmc9660::units::VelocityUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Command target velocity in the motor (rotor) frame.
 
bool getMotorVelocity (double &out_value, ::tmc9660::units::VelocityUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Read actual velocity in motor frame.
 
bool setMotorPosition (double value, ::tmc9660::units::PositionUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Command target position in motor frame.
 
bool getMotorPosition (double &out_value, ::tmc9660::units::PositionUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Read actual position in motor frame.
 
bool setLoadVelocity (double value, ::tmc9660::units::VelocityUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Command target velocity in the load (output-shaft) frame.
 
bool getLoadVelocity (double &out_value, ::tmc9660::units::VelocityUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Read actual velocity expressed in the load frame.
 
bool setLoadPosition (double value, ::tmc9660::units::PositionUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Command target position in the load frame (gearbox-aware).
 
bool getLoadPosition (double &out_value, ::tmc9660::units::PositionUnit unit, ::tmc9660::units::MotorContext const &ctx, uint8_t deviceIndex=ONBOARD_TMC9660_INDEX) noexcept
 Read actual position expressed in the load frame.
 
MotorError CreateOnboardDevice (BaseSpi &spiInterface, uint8_t address, const Tmc9660ControlPins &pins, const tmc9660::BootloaderConfig *bootCfg=nullptr) noexcept
 Create the onboard TMC9660 device using an SPI interface.
 
MotorError CreateOnboardDevice (BaseUart &uartInterface, uint8_t address, const Tmc9660ControlPins &pins, const tmc9660::BootloaderConfig *bootCfg=nullptr) noexcept
 Create the onboard TMC9660 device using a UART interface.
 
MotorError CreateExternalDevice (uint8_t csDeviceIndex, SpiDeviceId spiDeviceId, uint8_t address, const Tmc9660ControlPins &pins, const tmc9660::BootloaderConfig *bootCfg=nullptr) noexcept
 Create an external TMC9660 device on specified CS line (SPI).
 
MotorError CreateExternalDevice (uint8_t csDeviceIndex, BaseUart &uartInterface, uint8_t address, const Tmc9660ControlPins &pins, const tmc9660::BootloaderConfig *bootCfg=nullptr) noexcept
 Create an external TMC9660 device on a UART interface.
 
MotorError DeleteExternalDevice (uint8_t csDeviceIndex) noexcept
 Delete an external TMC9660 device.
 
uint8_t GetDeviceCount () const noexcept
 Get the number of active TMC9660 devices.
 
bool IsDeviceValid (uint8_t deviceIndex) const noexcept
 Check if a device index is valid and has an active device.
 
bool IsExternalSlotAvailable (uint8_t csDeviceIndex) const noexcept
 Check if an external device slot is available for creation.
 
void GetActiveDeviceIndices (std::array< uint8_t, MAX_TMC9660_DEVICES > &out, size_t &count) const noexcept
 Get indices of all active devices.
 
size_t InitializeAllDevices (std::array< bool, MAX_TMC9660_DEVICES > &results) noexcept
 Initialize all active devices.
 
void GetInitializationStatus (std::array< bool, MAX_TMC9660_DEVICES > &status) const noexcept
 Get initialization status of all device slots.
 
void DumpStatistics () const noexcept
 Dump comprehensive system statistics to log as INFO level. Logs all device statistics, handler status, and system health information.
 

Static Public Member Functions

static MotorController & GetInstance () noexcept
 Get the singleton instance.
 

Static Public Attributes

static constexpr uint8_t MAX_TMC9660_DEVICES = 4
 Maximum supported TMC9660 devices (board + external)
 
static constexpr uint8_t ONBOARD_TMC9660_INDEX = 0
 Onboard TMC9660 device index (SPI2_CS_TMC9660)
 
static constexpr uint8_t EXTERNAL_DEVICE_1_INDEX = 2
 External device 1 index (EXT_GPIO_CS_1)

 
static constexpr uint8_t EXTERNAL_DEVICE_2_INDEX = 3
 External device 2 index (EXT_GPIO_CS_2)
 

Private Member Functions

 MotorController ()
 
 ~MotorController ()=default
 
 MotorController (const MotorController &)=delete
 
MotorController & operator= (const MotorController &)=delete
 
bool Initialize ()
 Initialize all registered motor controller devices.
 
bool IsExternalDeviceIndex (uint8_t csDeviceIndex) const noexcept
 Validate if CS device index is for external device.
 
void UpdateLastError (MotorError error, int deviceIndex=-1) noexcept
 Store a new error code and update per-device counter.
 
void configureOnboardHostBusBeforeHandlerInitLocked (uint8_t device_index) noexcept
 
MotorError applyOnboardTmc9660HostSpiGateModeLocked (Tmc9660HostSpiGateMode mode) noexcept
 
void applyTmclSpiTimingLocked (uint8_t device_index) noexcept
 
template<typename Fn >
MotorError withReadyDriverLocked (uint8_t device_index, Fn &&fn) noexcept
 Run fn on the SPI driver of a ready device, through the handler (handler lock held).
 
void recordBatchLocked (uint8_t device_index, size_t accesses, size_t failed, uint32_t duration_us) noexcept
 Account one batch in accessStats_. Caller holds deviceMutex_.
 
template<typename Fn >
bool withSpiDriverLocked (uint8_t device_index, Fn &&fn) noexcept
 

Private Attributes

std::array< std::optional< Tmc9660Handler >, MAX_TMC9660_DEVICES > tmcHandlers_
 Handlers in place (no heap): a slot is emplaced by Create*Device() and reset on delete/deinit.
 
std::array< bool, MAX_TMC9660_DEVICES > deviceInitialized_
 
std::array< bool, MAX_TMC9660_DEVICES > deviceActive_
 Track which devices are active.
 
std::array< std::atomic< uint32_t >, MAX_TMC9660_DEVICES > deviceErrorCounts_ {}
 Per-device error counters.
 
std::array< MotorMechanics, MAX_TMC9660_DEVICES > mechanics_ {}
 Per-device mechanical mapping (default 1:1)
 
std::array< tmc9660::TmclSpiTiming, MAX_TMC9660_DEVICES > tmclSpiTiming_ {}
 Per-device SPI TMCL timing.
 
std::array< MotorAccessStats, MAX_TMC9660_DEVICES > accessStats_ {}
 Per-device TMCL access accounting.
 
bool onboardDeviceCreated_
 Track if onboard device has been created.
 
std::atomic< bool > initialized_
 Track if system has been initialized.
 
std::atomic< MotorError > last_error_ {MotorError::SUCCESS}
 Most recent error code.
 
RtosMutex deviceMutex_
 RTOS mutex for thread-safe device access.
 

Detailed Description

Singleton managing multiple TMC9660 motor controllers on Vortex V1.

Creates the onboard TMC9660 (index 0) and allows dynamic creation of external TMC9660 devices (indices 2–3). Provides array-based access to Tmc9660Handler instances, thread-safe device registration, and board-aware CS-pin management.

Note
Call CreateOnboardDevice() before EnsureInitialized(). Host-side GPIO control pins (RST, DRV_EN, FAULTN, WAKE) are required for the TMC9660 bootloader sequence.

Constructor & Destructor Documentation

◆ MotorController() [1/2]

MotorController::MotorController ( )
private

◆ ~MotorController()

MotorController::~MotorController ( )
privatedefault

◆ MotorController() [2/2]

MotorController::MotorController ( const MotorController &  )
privatedelete

Member Function Documentation

◆ ApplyOnboardTmc9660HostSpiGateMode()

MotorError MotorController::ApplyOnboardTmc9660HostSpiGateMode ( Tmc9660HostSpiGateMode  mode)
noexcept

Drive the onboard TMC9660 host SPI mux (PCAL_TMC_SPI_COMM_EN) on Vortex.

Parameters
modeDisabled for UART motor comms or to free the exported SPI segment; EnabledForHostSpiMotor before TMCL/bootloader over SPI.
Returns
MotorError::SUCCESS after applying drive levels (warnings only if a line is missing).
Note
Thread-safe. Call from application code when switching between SPI and UART transports without tearing down the handler (requires coordinated handler re-creation today).
Here is the caller graph for this function:

◆ applyOnboardTmc9660HostSpiGateModeLocked()

MotorError MotorController::applyOnboardTmc9660HostSpiGateModeLocked ( Tmc9660HostSpiGateMode  mode)
privatenoexcept
Precondition
deviceMutex_ held.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ApplyParameterImage()

MotorError MotorController::ApplyParameterImage ( std::span< const MotorParameterWrite >  image,
const MotorApplyOptions &  options,
MotorApplyResult &  result,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Apply a parameter image (MOTOR_TYPE first), optionally only where the chip differs and with read-back verification. The TMC9660's own NVM is not used (design §4.7).

Parameters
[out]resultCounts of written, skipped, rejected and mismatched entries, and the first bad one.
Returns
SUCCESS when result.ok(); COMMUNICATION_FAILED otherwise.
Here is the call graph for this function:

◆ applyTmclSpiTimingLocked()

void MotorController::applyTmclSpiTimingLocked ( uint8_t  device_index)
privatenoexcept
Precondition
deviceMutex_ held. Pushes the stored SPI TMCL timing of device_index to its driver.
Here is the caller graph for this function:

◆ ClearLatchedFaults()

MotorError MotorController::ClearLatchedFaults ( uint32_t  general_errors,
uint32_t  gate_driver_errors,
uint32_t  adc_status,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Clear latched (write-1-to-clear) bits of the general error, gate-driver and ADC words.

Here is the call graph for this function:

◆ configureOnboardHostBusBeforeHandlerInitLocked()

void MotorController::configureOnboardHostBusBeforeHandlerInitLocked ( uint8_t  device_index)
privatenoexcept
Precondition
deviceMutex_ held. Applies PCAL SPI mux for slot device_index before handler init.
Here is the caller graph for this function:

◆ CreateExternalDevice() [1/2]

MotorError MotorController::CreateExternalDevice ( uint8_t  csDeviceIndex,
BaseUart &  uartInterface,
uint8_t  address,
const Tmc9660ControlPins &  pins,
const tmc9660::BootloaderConfig *  bootCfg = nullptr 
)
noexcept

Create an external TMC9660 device on a UART interface.

Parameters
csDeviceIndexExternal device slot index (2 or 3 only)
uartInterfaceReference to BaseUart implementation
addressTMC9660 device address
pinsHost-side GPIO control pin references for the TMC9660
bootCfgOptional bootloader config (defaults to kDefaultBootConfig)
Returns
MotorError::SUCCESS if device created, typed error otherwise
Note
Only EXTERNAL_DEVICE_1_INDEX (2) and EXTERNAL_DEVICE_2_INDEX (3) are allowed

◆ CreateExternalDevice() [2/2]

MotorError MotorController::CreateExternalDevice ( uint8_t  csDeviceIndex,
SpiDeviceId  spiDeviceId,
uint8_t  address,
const Tmc9660ControlPins &  pins,
const tmc9660::BootloaderConfig *  bootCfg = nullptr 
)
noexcept

Create an external TMC9660 device on specified CS line (SPI).

Parameters
csDeviceIndexExternal device CS index (2 or 3 only)
spiDeviceIdSPI device ID for communication (EXTERNAL_DEVICE_1 or EXTERNAL_DEVICE_2)
addressTMC9660 device address
pinsHost-side GPIO control pin references for the TMC9660
bootCfgOptional bootloader config (defaults to kDefaultBootConfig)
Returns
MotorError::SUCCESS if device created, typed error otherwise
Note
Only EXTERNAL_DEVICE_1_INDEX (2) and EXTERNAL_DEVICE_2_INDEX (3) are allowed
Here is the call graph for this function:

◆ CreateOnboardDevice() [1/2]

MotorError MotorController::CreateOnboardDevice ( BaseSpi &  spiInterface,
uint8_t  address,
const Tmc9660ControlPins &  pins,
const tmc9660::BootloaderConfig *  bootCfg = nullptr 
)
noexcept

Create the onboard TMC9660 device using an SPI interface.

Parameters
spiInterfaceReference to BaseSpi implementation
addressTMC9660 device address
pinsHost-side GPIO control pin references for the TMC9660
bootCfgOptional bootloader config (defaults to kDefaultBootConfig)
Returns
MotorError::SUCCESS if device created, typed error otherwise
Note
Must be called before Initialize() so the onboard device is registered. The GPIO pins must already be configured (pin number, direction).
Here is the caller graph for this function:

◆ CreateOnboardDevice() [2/2]

MotorError MotorController::CreateOnboardDevice ( BaseUart &  uartInterface,
uint8_t  address,
const Tmc9660ControlPins &  pins,
const tmc9660::BootloaderConfig *  bootCfg = nullptr 
)
noexcept

Create the onboard TMC9660 device using a UART interface.

Parameters
uartInterfaceReference to BaseUart implementation
addressTMC9660 device address
pinsHost-side GPIO control pin references for the TMC9660
bootCfgOptional bootloader config (defaults to kDefaultBootConfig)
Returns
MotorError::SUCCESS if device created, typed error otherwise
Note
Must be called before Initialize() so the onboard device is registered. The GPIO pins must already be configured (pin number, direction).

◆ Deinitialize()

bool MotorController::Deinitialize ( )
noexcept

Shut down all motor controller devices and release resources.

Returns
true on success
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DeleteExternalDevice()

MotorError MotorController::DeleteExternalDevice ( uint8_t  csDeviceIndex)
noexcept

Delete an external TMC9660 device.

Parameters
csDeviceIndexExternal device CS index (2 or 3 only)
Returns
MotorError::SUCCESS if device deleted, typed error otherwise
Note
Cannot delete onboard device (index 0). Only external devices can be deleted.

◆ DumpStatistics()

void MotorController::DumpStatistics ( ) const
noexcept

Dump comprehensive system statistics to log as INFO level. Logs all device statistics, handler status, and system health information.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ EnsureInitialized()

bool MotorController::EnsureInitialized ( )
inlinenoexcept

Initialise all registered motor controllers if not already done.

Returns
true on success or if already initialised.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetAccessStats()

MotorError MotorController::GetAccessStats ( MotorAccessStats &  stats,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
) const
noexcept

TMCL access accounting of one device (batches, accesses, failures, batch latency).

◆ GetActiveDeviceIndices()

void MotorController::GetActiveDeviceIndices ( std::array< uint8_t, MAX_TMC9660_DEVICES > &  out,
size_t &  count 
) const
noexcept

Get indices of all active devices.

Parameters
outFixed-size output array
countOutput: number of entries written
Here is the caller graph for this function:

◆ GetDeviceCount()

uint8_t MotorController::GetDeviceCount ( ) const
noexcept

Get the number of active TMC9660 devices.

Returns
Number of active devices (1 to MAX_TMC9660_DEVICES)
Note
Always includes onboard device, plus any active external devices
Here is the caller graph for this function:

◆ GetInitializationStatus()

void MotorController::GetInitializationStatus ( std::array< bool, MAX_TMC9660_DEVICES > &  status) const
noexcept

Get initialization status of all device slots.

Parameters
statusOutput: per-slot status

◆ GetInstance()

MotorController & MotorController::GetInstance ( )
staticnoexcept

Get the singleton instance.

Returns
Reference to the singleton MotorController.
Here is the caller graph for this function:

◆ GetLastError()

MotorError MotorController::GetLastError ( ) const
inlinenoexcept

Get the most recent error code.

Returns
Last MotorError set by any API call

◆ getLoadPosition()

bool MotorController::getLoadPosition ( double &  out_value,
::tmc9660::units::PositionUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Read actual position expressed in the load frame.

◆ getLoadVelocity()

bool MotorController::getLoadVelocity ( double &  out_value,
::tmc9660::units::VelocityUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Read actual velocity expressed in the load frame.

◆ getMotorMechanics()

MotorMechanics MotorController::getMotorMechanics ( uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX) const
noexcept

Read back the currently configured mechanical mapping.

Parameters
deviceIndexDevice slot (defaults to onboard)

◆ getMotorPosition()

bool MotorController::getMotorPosition ( double &  out_value,
::tmc9660::units::PositionUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Read actual position in motor frame.

◆ getMotorVelocity()

bool MotorController::getMotorVelocity ( double &  out_value,
::tmc9660::units::VelocityUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Read actual velocity in motor frame.

◆ GetSystemDiagnostics()

MotorError MotorController::GetSystemDiagnostics ( MotorSystemDiagnostics &  diagnostics) const
noexcept

Fill a diagnostics snapshot with current system state.

Parameters
diagnosticsOutput structure to populate
Returns
MotorError::SUCCESS on success, MotorError::NOT_INITIALIZED if not init

◆ GetTmclSpiStats()

MotorError MotorController::GetTmclSpiStats ( tmc9660::TmclSpiStats &  stats,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

SPI TMCL transport counters (frames, NOT_READY re-sends, stale replies, failures).

Returns
SUCCESS, INVALID_DEVICE_INDEX, DEVICE_NOT_FOUND (no device or driver not up), or INVALID_PARAMETER for a UART device.

◆ GetTmclSpiTiming()

MotorError MotorController::GetTmclSpiTiming ( tmc9660::TmclSpiTiming &  timing,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
) const
noexcept

SPI TMCL timing stored for a slot (the driver defaults unless changed).

Returns
SUCCESS or INVALID_DEVICE_INDEX.

◆ handler()

Tmc9660Handler * MotorController::handler ( uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX)
noexcept

Get access to Tmc9660Handler by device index.

Parameters
deviceIndexDevice index (0=onboard, 2-3=external)
Returns
Pointer to Tmc9660Handler if valid and active, nullptr otherwise
Note
Returns nullptr if deviceIndex is invalid, device not active, or not initialized
Here is the caller graph for this function:

◆ Initialize()

bool MotorController::Initialize ( )
private

Initialize all registered motor controller devices.

Calls Tmc9660Handler::Initialize() on every active device. Devices must be created first via CreateOnboardDevice() / CreateExternalDevice() which supply the required host-side GPIO control pins.

Returns
true if all active devices initialized successfully, false otherwise
Here is the call graph for this function:
Here is the caller graph for this function:

◆ InitializeAllDevices()

size_t MotorController::InitializeAllDevices ( std::array< bool, MAX_TMC9660_DEVICES > &  results)
noexcept

Initialize all active devices.

Parameters
resultsOutput: per-slot initialization result
Returns
Number of devices successfully initialized

◆ IsDeviceValid()

bool MotorController::IsDeviceValid ( uint8_t  deviceIndex) const
noexcept

Check if a device index is valid and has an active device.

Parameters
deviceIndexDevice index to check
Returns
true if device exists and is active, false otherwise

◆ IsExternalDeviceIndex()

bool MotorController::IsExternalDeviceIndex ( uint8_t  csDeviceIndex) const
privatenoexcept

Validate if CS device index is for external device.

Parameters
csDeviceIndexDevice index to validate
Returns
true if index is for external device (2 or 3), false otherwise

◆ IsExternalSlotAvailable()

bool MotorController::IsExternalSlotAvailable ( uint8_t  csDeviceIndex) const
noexcept

Check if an external device slot is available for creation.

Parameters
csDeviceIndexExternal device CS index (2 or 3 only)
Returns
true if slot is available, false if occupied or invalid index

◆ IsInitialized()

bool MotorController::IsInitialized ( ) const
inlinenoexcept

Check whether the motor controller subsystem has been initialised.

Returns
true if initialised.
Here is the caller graph for this function:

◆ operator=()

MotorController & MotorController::operator= ( const MotorController &  )
privatedelete

◆ ReadDriveDiagnostics()

MotorError MotorController::ReadDriveDiagnostics ( MotorDriveDiagnostics &  out,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Read the four status words (289/299/300/301) in one batch and decode them.

Includes the chip-reset indication (watchdog bit: re-apply the parameter image), heartbeat stop, gate-driver faults per phase and side, I²t and thermal conditions, and a severity. On SPI the TMCL link counters are copied into out as well.

Here is the call graph for this function:

◆ ReadParameters()

MotorError MotorController::ReadParameters ( std::span< MotorParameterAccess >  items,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Read several axis parameters in pipelined batches (GAP).

Parameters
itemsIn: ids (and axis). Out: values and per-element TMCL status.
Here is the call graph for this function:

◆ ReadTelemetry()

MotorError MotorController::ReadTelemetry ( MotorTelemetrySet  set,
MotorTelemetrySnapshot &  snapshot,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Refresh a caller-owned telemetry snapshot from one batch.

MotorTelemetrySet::Cyclic is the drive-observability design's fixed slots (position, velocity, packed Iq/Id, status, and the error words alternating); Status adds power, thermal and I²t words; Full reads every field. Each field keeps the sequence number of its last good read, so a consumer can judge its age (MotorTelemetrySnapshot::Age()).

Here is the call graph for this function:

◆ recordBatchLocked()

void MotorController::recordBatchLocked ( uint8_t  device_index,
size_t  accesses,
size_t  failed,
uint32_t  duration_us 
)
privatenoexcept

Account one batch in accessStats_. Caller holds deviceMutex_.

◆ ResetAccessStats()

void MotorController::ResetAccessStats ( uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX)
noexcept

Zero the access accounting of one device.

◆ ResetTmclSpiStats()

MotorError MotorController::ResetTmclSpiStats ( uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX)
noexcept

Reset the SPI TMCL transport counters.

Returns
Same codes as GetTmclSpiStats().

◆ setLoadPosition()

bool MotorController::setLoadPosition ( double  value,
::tmc9660::units::PositionUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Command target position in the load frame (gearbox-aware).

◆ setLoadVelocity()

bool MotorController::setLoadVelocity ( double  value,
::tmc9660::units::VelocityUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Command target velocity in the load (output-shaft) frame.

The value is multiplied by gear_ratio_motor_to_output (and sign-flipped if invert_output is set) before being forwarded to the silicon. With the default 1:1 mechanics this is identical to setMotorVelocity().

Convention example: a 10:1 reducer with gear_ratio_motor_to_output = 10 and setLoadVelocity(60.0, VelocityUnit::Rpm, ctx) will spin the rotor at 600 RPM so the output turns at 60 RPM.

◆ setMotorMechanics()

bool MotorController::setMotorMechanics ( const MotorMechanics &  mech,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Set the mechanical mapping (gearbox + direction) for a device.

The mapping is per-device, lives in the manager, and applies to every subsequent setLoadXxx / getLoadXxx call. Default mapping is direct-drive (gear_ratio=1.0, invert=false), in which load-frame == motor-frame.

Parameters
mechMechanical configuration
deviceIndexDevice slot (defaults to onboard)
Returns
true if accepted, false if deviceIndex is invalid or mech.gear_ratio_motor_to_output is non-positive / non-finite.
Here is the call graph for this function:

◆ setMotorPosition()

bool MotorController::setMotorPosition ( double  value,
::tmc9660::units::PositionUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Command target position in motor frame.

◆ setMotorVelocity()

bool MotorController::setMotorVelocity ( double  value,
::tmc9660::units::VelocityUnit  unit,
::tmc9660::units::MotorContext const &  ctx,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Command target velocity in the motor (rotor) frame.

This is a thin convenience wrapper around the vendor unit-aware API: the value is forwarded straight to velocityControl.setTargetVelocity with no gearbox math applied. Use this when you want to think in the frame the silicon thinks in.

◆ SetTmclSpiTiming()

MotorError MotorController::SetTmclSpiTiming ( const tmc9660::TmclSpiTiming &  timing,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Set the SPI TMCL handshake timing for one TMC9660.

The driver polls the TMC9660's NOT_READY status instead of waiting a fixed gap (see tmc9660::TmclSpiTiming). Every device starts with the driver defaults (TmclSpiTiming{}: no frame gap, 20 to 200 us back-off, 60 polls). The value is stored per slot and applied to the driver now if it is up, and again after every (re)initialization of that slot, so it can be set before Vortex brings the motors up. Measure with the TMC9660 driver's tmcl_timing example before changing it; tmc9660::TmclSpiTiming::legacy() restores the 1000 us gap used up to driver 1.0.0.

The poll budget runs on the calling thread. Lower maxPolls for slots that are accessed from a high-priority control task.

Parameters
timingNew timing.
deviceIndexDevice index (0=onboard, 2-3=external).
Returns
SUCCESS, INVALID_DEVICE_INDEX, or INVALID_PARAMETER when the active device on that slot talks UART (the setting only affects SPI).

◆ TryGetOnboardCommMode()

std::optional< tmc9660::CommMode > MotorController::TryGetOnboardCommMode ( ) const
noexcept

Physical comm mode for the onboard handler (slot 0), if present.

Returns
CommMode::SPI or ::UART from the active Tmc9660Handler, or nullopt when no device.

◆ UpdateLastError()

void MotorController::UpdateLastError ( MotorError  error,
int  deviceIndex = -1 
)
privatenoexcept

Store a new error code and update per-device counter.

Parameters
errorThe error to record
deviceIndexOptional device index to bump its error counter
Here is the caller graph for this function:

◆ visitDriver()

template<typename Func >
auto MotorController::visitDriver ( Func &&  func,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
inlinenoexcept

Visit the underlying typed TMC9660 driver by device index.

Since the TMC9660 driver is now template-based, direct pointer access is replaced by a visitor pattern. The provided function receives a reference to the typed driver (either TMC9660<HalSpiTmc9660Comm> or TMC9660<HalUartTmc9660Comm>).

Template Parameters
FuncCallable type accepting auto& (the typed driver reference)
Parameters
funcThe visitor function to execute on the driver
deviceIndexDevice index (0=onboard, 2-3=external)
Returns
The return value of func, or default-constructed if driver not available
motorController.visitDriver([](auto& driver) {
driver.motorConfig.setType(tmc9660::tmcl::MotorType::BLDC, 7);
});
Here is the caller graph for this function:

◆ withReadyDriverLocked()

template<typename Fn >
MotorError MotorController::withReadyDriverLocked ( uint8_t  device_index,
Fn &&  fn 
)
inlineprivatenoexcept

Run fn on the SPI driver of a ready device, through the handler (handler lock held).

Precondition
deviceMutex_ held. SPI driver of a slot whose handler is initialized, or nullptr.
Returns
true if fn ran (device active, driver up, transport is SPI). Caller holds deviceMutex_.

Run fn(driver) on a ready device through the handler, SPI or UART (handler lock held).

Returns
SUCCESS, or why it did not run (DEVICE_NOT_FOUND / NOT_INITIALIZED). Caller holds deviceMutex_.
Here is the call graph for this function:

◆ withSpiDriverLocked()

template<typename Fn >
bool MotorController::withSpiDriverLocked ( uint8_t  device_index,
Fn &&  fn 
)
inlineprivatenoexcept

◆ WriteParameters()

MotorError MotorController::WriteParameters ( std::span< MotorParameterAccess >  items,
uint8_t  deviceIndex = ONBOARD_TMC9660_INDEX 
)
noexcept

Write several axis parameters in pipelined batches (SAP); per-element status in items.

Here is the call graph for this function:

Member Data Documentation

◆ accessStats_

std::array<MotorAccessStats, MAX_TMC9660_DEVICES> MotorController::accessStats_ {}
private

Per-device TMCL access accounting.

◆ deviceActive_

std::array<bool, MAX_TMC9660_DEVICES> MotorController::deviceActive_
private

Track which devices are active.

◆ deviceErrorCounts_

std::array<std::atomic<uint32_t>, MAX_TMC9660_DEVICES> MotorController::deviceErrorCounts_ {}
private

Per-device error counters.

◆ deviceInitialized_

std::array<bool, MAX_TMC9660_DEVICES> MotorController::deviceInitialized_
private

◆ deviceMutex_

RtosMutex MotorController::deviceMutex_
mutableprivate

RTOS mutex for thread-safe device access.

◆ EXTERNAL_DEVICE_1_INDEX

constexpr uint8_t MotorController::EXTERNAL_DEVICE_1_INDEX = 2
staticconstexpr

External device 1 index (EXT_GPIO_CS_1)

◆ EXTERNAL_DEVICE_2_INDEX

constexpr uint8_t MotorController::EXTERNAL_DEVICE_2_INDEX = 3
staticconstexpr

External device 2 index (EXT_GPIO_CS_2)

◆ initialized_

std::atomic<bool> MotorController::initialized_
private

Track if system has been initialized.

◆ last_error_

std::atomic<MotorError> MotorController::last_error_ {MotorError::SUCCESS}
private

Most recent error code.

◆ MAX_TMC9660_DEVICES

constexpr uint8_t MotorController::MAX_TMC9660_DEVICES = 4
staticconstexpr

Maximum supported TMC9660 devices (board + external)

◆ mechanics_

std::array<MotorMechanics, MAX_TMC9660_DEVICES> MotorController::mechanics_ {}
private

Per-device mechanical mapping (default 1:1)

◆ ONBOARD_TMC9660_INDEX

constexpr uint8_t MotorController::ONBOARD_TMC9660_INDEX = 0
staticconstexpr

Onboard TMC9660 device index (SPI2_CS_TMC9660)

◆ onboardDeviceCreated_

bool MotorController::onboardDeviceCreated_
private

Track if onboard device has been created.

◆ tmcHandlers_

std::array<std::optional<Tmc9660Handler>, MAX_TMC9660_DEVICES> MotorController::tmcHandlers_
private

Handlers in place (no heap): a slot is emplaced by Create*Device() and reset on delete/deinit.

◆ tmclSpiTiming_

std::array<tmc9660::TmclSpiTiming, MAX_TMC9660_DEVICES> MotorController::tmclSpiTiming_ {}
private

Per-device SPI TMCL timing.


The documentation for this class was generated from the following files: