|
HardFOC Vortex V1 HAL
Unified HAL API for the HardFOC Vortex motor-control platform (ESP32-C6, TMC9660)
|
Singleton managing multiple TMC9660 motor controllers on Vortex V1. More...
#include <MotorController.h>
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. | |
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.
|
private |
|
privatedefault |
|
privatedelete |
|
noexcept |
Drive the onboard TMC9660 host SPI mux (PCAL_TMC_SPI_COMM_EN) on Vortex.
| mode | Disabled for UART motor comms or to free the exported SPI segment; EnabledForHostSpiMotor before TMCL/bootloader over SPI. |
|
privatenoexcept |
deviceMutex_ held.
|
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).
| [out] | result | Counts of written, skipped, rejected and mismatched entries, and the first bad one. |
|
privatenoexcept |
deviceMutex_ held. Pushes the stored SPI TMCL timing of device_index to its driver.
|
noexcept |
Clear latched (write-1-to-clear) bits of the general error, gate-driver and ADC words.
|
privatenoexcept |
deviceMutex_ held. Applies PCAL SPI mux for slot device_index before handler init.
|
noexcept |
Create an external TMC9660 device on a UART interface.
| csDeviceIndex | External device slot index (2 or 3 only) |
| uartInterface | Reference to BaseUart implementation |
| address | TMC9660 device address |
| pins | Host-side GPIO control pin references for the TMC9660 |
| bootCfg | Optional bootloader config (defaults to kDefaultBootConfig) |
|
noexcept |
Create an external TMC9660 device on specified CS line (SPI).
| csDeviceIndex | External device CS index (2 or 3 only) |
| spiDeviceId | SPI device ID for communication (EXTERNAL_DEVICE_1 or EXTERNAL_DEVICE_2) |
| address | TMC9660 device address |
| pins | Host-side GPIO control pin references for the TMC9660 |
| bootCfg | Optional bootloader config (defaults to kDefaultBootConfig) |
|
noexcept |
Create the onboard TMC9660 device using an SPI interface.
| spiInterface | Reference to BaseSpi implementation |
| address | TMC9660 device address |
| pins | Host-side GPIO control pin references for the TMC9660 |
| bootCfg | Optional bootloader config (defaults to kDefaultBootConfig) |
|
noexcept |
Create the onboard TMC9660 device using a UART interface.
| uartInterface | Reference to BaseUart implementation |
| address | TMC9660 device address |
| pins | Host-side GPIO control pin references for the TMC9660 |
| bootCfg | Optional bootloader config (defaults to kDefaultBootConfig) |
|
noexcept |
Shut down all motor controller devices and release resources.
|
noexcept |
Delete an external TMC9660 device.
| csDeviceIndex | External device CS index (2 or 3 only) |
|
noexcept |
Dump comprehensive system statistics to log as INFO level. Logs all device statistics, handler status, and system health information.
|
inlinenoexcept |
Initialise all registered motor controllers if not already done.
|
noexcept |
TMCL access accounting of one device (batches, accesses, failures, batch latency).
|
noexcept |
Get indices of all active devices.
| out | Fixed-size output array |
| count | Output: number of entries written |
|
noexcept |
Get the number of active TMC9660 devices.
|
noexcept |
Get initialization status of all device slots.
| status | Output: per-slot status |
|
staticnoexcept |
Get the singleton instance.
|
inlinenoexcept |
Get the most recent error code.
|
noexcept |
Read actual position expressed in the load frame.
|
noexcept |
Read actual velocity expressed in the load frame.
|
noexcept |
Read back the currently configured mechanical mapping.
| deviceIndex | Device slot (defaults to onboard) |
|
noexcept |
Read actual position in motor frame.
|
noexcept |
Read actual velocity in motor frame.
|
noexcept |
Fill a diagnostics snapshot with current system state.
| diagnostics | Output structure to populate |
|
noexcept |
SPI TMCL transport counters (frames, NOT_READY re-sends, stale replies, failures).
|
noexcept |
SPI TMCL timing stored for a slot (the driver defaults unless changed).
|
noexcept |
Get access to Tmc9660Handler by device index.
| deviceIndex | Device index (0=onboard, 2-3=external) |
|
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.
|
noexcept |
Initialize all active devices.
| results | Output: per-slot initialization result |
|
noexcept |
Check if a device index is valid and has an active device.
| deviceIndex | Device index to check |
|
privatenoexcept |
Validate if CS device index is for external device.
| csDeviceIndex | Device index to validate |
|
noexcept |
Check if an external device slot is available for creation.
| csDeviceIndex | External device CS index (2 or 3 only) |
|
inlinenoexcept |
Check whether the motor controller subsystem has been initialised.
|
privatedelete |
|
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.
|
noexcept |
Read several axis parameters in pipelined batches (GAP).
| items | In: ids (and axis). Out: values and per-element TMCL status. |
|
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()).
|
privatenoexcept |
Account one batch in accessStats_. Caller holds deviceMutex_.
|
noexcept |
Zero the access accounting of one device.
|
noexcept |
Reset the SPI TMCL transport counters.
|
noexcept |
Command target position in the load frame (gearbox-aware).
|
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.
|
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.
| mech | Mechanical configuration |
| deviceIndex | Device slot (defaults to onboard) |
deviceIndex is invalid or mech.gear_ratio_motor_to_output is non-positive / non-finite.
|
noexcept |
Command target position in motor frame.
|
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.
|
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.
| timing | New timing. |
| deviceIndex | Device index (0=onboard, 2-3=external). |
|
noexcept |
Physical comm mode for the onboard handler (slot 0), if present.
CommMode::SPI or ::UART from the active Tmc9660Handler, or nullopt when no device.
|
privatenoexcept |
Store a new error code and update per-device counter.
| error | The error to record |
| deviceIndex | Optional device index to bump its error counter |
|
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>).
| Func | Callable type accepting auto& (the typed driver reference) |
| func | The visitor function to execute on the driver |
| deviceIndex | Device index (0=onboard, 2-3=external) |
|
inlineprivatenoexcept |
Run fn on the SPI driver of a ready device, through the handler (handler lock held).
deviceMutex_ held. SPI driver of a slot whose handler is initialized, or nullptr. 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).
|
inlineprivatenoexcept |
|
noexcept |
Write several axis parameters in pipelined batches (SAP); per-element status in items.
|
private |
Per-device TMCL access accounting.
|
private |
Track which devices are active.
|
private |
Per-device error counters.
|
private |
|
mutableprivate |
RTOS mutex for thread-safe device access.
|
staticconstexpr |
External device 1 index (EXT_GPIO_CS_1)
|
staticconstexpr |
External device 2 index (EXT_GPIO_CS_2)
|
private |
Track if system has been initialized.
|
private |
Most recent error code.
|
staticconstexpr |
Maximum supported TMC9660 devices (board + external)
|
private |
Per-device mechanical mapping (default 1:1)
|
staticconstexpr |
Onboard TMC9660 device index (SPI2_CS_TMC9660)
|
private |
Track if onboard device has been created.
|
private |
Handlers in place (no heap): a slot is emplaced by Create*Device() and reset on delete/deinit.
|
private |
Per-device SPI TMCL timing.