HF-Core Platform 0.1.0-dev
Hardware-Agnostic Handler Layer & RTOS Utilities for HardFOC
Loading...
Searching...
No Matches
Tmc9660Handler Class Reference

Unified, non-templated handler for a single TMC9660 motor controller device. More...

#include <Tmc9660Handler.h>

Collaboration diagram for Tmc9660Handler:
[legend]

Classes

class  Adc
 BaseAdc adapter for all TMC9660 ADC channels. More...
 
class  Gpio
 BaseGpio adapter for a single TMC9660 internal GPIO channel. More...
 
class  Temperature
 BaseTemperature adapter for the TMC9660 internal chip temperature sensor. More...
 

Public Types

Type Aliases
using SpiDriver = tmc9660::TMC9660<HalSpiTmc9660Comm>
 TMC9660 driver instantiated with SPI communication.
 
using UartDriver = tmc9660::TMC9660<HalUartTmc9660Comm>
 TMC9660 driver instantiated with UART communication.
 
using DriverVariant = std::variant<std::monostate, SpiDriver*, UartDriver*>
 Non-owning variant holding either driver type or empty (monostate).
 
using ConstDriverVariant = std::variant<std::monostate, const SpiDriver*, const UartDriver*>
 Const version of DriverVariant.
 

Public Member Functions

Construction and Destruction
 Tmc9660Handler (BaseSpi &spi, BaseGpio &rst, BaseGpio &drv_en, BaseGpio &faultn, BaseGpio &wake, uint8_t address=0, const tmc9660::BootloaderConfig *bootCfg=&kDefaultBootConfig)
 Construct a handler for a TMC9660 connected via SPI.
 
 Tmc9660Handler (BaseUart &uart, BaseGpio &rst, BaseGpio &drv_en, BaseGpio &faultn, BaseGpio &wake, uint8_t address=0, const tmc9660::BootloaderConfig *bootCfg=&kDefaultBootConfig)
 Construct a handler for a TMC9660 connected via UART.
 
 ~Tmc9660Handler () noexcept
 Destructor. Releases the driver, communication adapter, and all wrappers.
 
 Tmc9660Handler (const Tmc9660Handler &)=delete
 Non-copyable.
 
Tmc9660Handleroperator= (const Tmc9660Handler &)=delete
 Non-copyable.
 
 Tmc9660Handler (Tmc9660Handler &&)=delete
 Non-movable.
 
Tmc9660Handleroperator= (Tmc9660Handler &&)=delete
 Non-movable.
 
Initialization
bool Initialize (bool performReset=true, bool retrieveBootloaderInfo=true, bool failOnVerifyError=false)
 Initialize the TMC9660 device.
 
bool EnsureInitialized () noexcept
 Ensure driver is initialized (lazy init entrypoint).
 
bool IsDriverReady () const noexcept
 Check if the TMC9660 driver has been initialized and is ready.
 
Peripheral Accessors

Retrieve references to the handler's internal peripheral wrappers.

Gpiogpio (uint8_t gpioNumber)
 Get a reference to the GPIO wrapper for a TMC9660 internal GPIO pin.
 
Adcadc ()
 Get a reference to the ADC wrapper.
 
Temperaturetemperature ()
 Get a reference to the Temperature wrapper.
 
Communication Info
tmc9660::CommMode GetCommMode () const noexcept
 Get the communication mode in use (SPI or UART).
 
const tmc9660::BootloaderConfig & bootConfig () const noexcept
 Get the bootloader configuration in use.
 
Diagnostics
void DumpDiagnostics () noexcept
 Dump comprehensive diagnostics to the system log.
 
const charGetDescription () const noexcept
 Get a human-readable description of the handler.
 
Typed Driver Access

Direct access to the underlying typed TMC9660 driver instance.

SpiDriverdriverViaSpi () noexcept
 Get the TMC9660 driver instance connected via SPI (nullptr if using UART).
 
const SpiDriverdriverViaSpi () const noexcept
 Const version of driverViaSpi().
 
UartDriverdriverViaUart () noexcept
 Get the TMC9660 driver instance connected via UART (nullptr if using SPI).
 
const UartDriverdriverViaUart () const noexcept
 Const version of driverViaUart().
 
DriverVariant GetDriver () noexcept
 Get the active driver pointer without requiring visitor usage.
 
ConstDriverVariant GetDriver () const noexcept
 Const overload of GetDriver().
 
Driver Visitor Pattern

Generic access when comm mode is not known at the call site.

template<typename Func >
auto visitDriver (Func &&func) noexcept
 Execute a function on the underlying typed TMC9660 driver.
 
template<typename Func >
auto visitDriver (Func &&func) const noexcept
 Const overload of visitDriver().
 

Static Public Attributes

Default Configuration
static const tmc9660::BootloaderConfig kDefaultBootConfig
 Default bootloader configuration based on TMC9660-3PH-EVAL board settings.
 

Private Member Functions

bool EnsureInitializedLocked () noexcept
 Ensure initialized while handler_mutex_ is already held.
 
template<typename Func >
auto visitDriverInternal (Func &&func) noexcept
 Internal no-lock visitDriver for use from already-locked contexts.
 

Private Attributes

bool use_spi_
 true = SPI communication active, false = UART.
 
char description_ [64] {}
 Human-readable handler description.
 
Communication Adapters

One of these is created at construction time; the other remains null.

std::unique_ptr< HalSpiTmc9660Commspi_comm_
 SPI communication adapter (or nullptr).
 
std::unique_ptr< HalUartTmc9660Commuart_comm_
 UART communication adapter (or nullptr).
 
Typed Driver Instances

Created during Initialize(); one is active, the other remains null.

std::unique_ptr< SpiDriverspi_driver_
 TMC9660 driver for SPI mode (or nullptr).
 
std::unique_ptr< UartDriveruart_driver_
 TMC9660 driver for UART mode (or nullptr).
 
Peripheral Wrappers

Created during Initialize().

std::array< std::unique_ptr< Gpio >, 2 > gpioWrappers_
 GPIO17 [0] and GPIO18 [1].
 
std::unique_ptr< AdcadcWrapper_
 Multi-channel ADC wrapper.
 
std::unique_ptr< TemperaturetemperatureWrapper_
 Chip temperature wrapper.
 
Configuration
const tmc9660::BootloaderConfig * bootCfg_
 Bootloader config (not owned; must outlive handler).
 
uint8_t device_address_
 7-bit TMCL device address.
 
Thread Safety
RtosMutex handler_mutex_
 Recursive mutex for thread-safe access.
 

Detailed Description

Unified, non-templated handler for a single TMC9660 motor controller device.

Tmc9660Handler is the primary interface that application and manager code uses to interact with a TMC9660 motor driver. It hides the template complexity of tmc9660::TMC9660<CommType> behind a clean, polymorphism-free API.

Design Decisions

  • Non-templated: The handler uses visitDriver() to route calls to the correct typed driver (SPI or UART). This prevents template propagation through the codebase and allows MotorController to store handlers in a plain std::unique_ptr<Tmc9660Handler>.
  • Control pins required: The TMC9660 bootloader initialization sequence requires host-side GPIO control of four pins (RST, DRV_EN, FAULTN, WAKE). These must be provided at construction time.
  • Lazy driver creation: The typed TMC9660 driver instance is created during Initialize(), not in the constructor. This saves memory until initialization is actually needed.
  • Peripheral wrappers: Inner classes Gpio, Adc, and Temperature implement the HardFOC base interfaces, making TMC9660 peripherals available to the manager layer (GpioManager, AdcManager, TemperatureManager) through the standard abstractions.

Usage Example

// Construction (SPI mode, address 1, default boot config)
// Initialize (performs hardware reset and bootloader configuration)
Logger::GetInstance().Error("App", "TMC9660 init failed!");
return;
}
// Motor control via typed driver pointer
auto* drv = handler.driverViaSpi();
drv->motorConfig.setType(tmcl::MotorType::BLDC_MOTOR, 7);
drv->commutation.setMode(tmcl::CommutationMode::FOC);
drv->motorControl.enable();
drv->velocityControl.setTargetVelocity(1000);
// Telemetry via driver
float supply_v = drv->telemetry.getSupplyVoltage();
float chip_temp = drv->telemetry.getChipTemperature();
// GPIO (TMC9660 internal GPIO17)
handler.gpio(17).SetPinLevel(hf_gpio_level_t::HF_GPIO_LEVEL_HIGH);
// ADC (read AIN channel 0)
float voltage;
handler.adc().ReadChannelV(0, voltage);
// Generic driver access via visitor
handler.visitDriver([](auto& driver) {
driver.feedbackSense.configureHallSensor(1, 2, 3);
driver.protection.setOvertemperatureLimit(120.0f);
});
static Logger & GetInstance() noexcept
Get singleton instance.
Definition Logger.cpp:61
void Error(const char *tag, const char *format,...) noexcept
Log error message.
Definition Logger.cpp:170
bool Initialize() noexcept override
Initialize (verifies parent driver is ready).
Definition Tmc9660Handler.cpp:815
Unified, non-templated handler for a single TMC9660 motor controller device.
Definition Tmc9660Handler.h:452
Temperature & temperature()
Get a reference to the Temperature wrapper.
Definition Tmc9660Handler.cpp:395
See also
HalSpiTmc9660Comm SPI communication adapter
HalUartTmc9660Comm UART communication adapter
MotorController Multi-device manager that owns Tmc9660Handler instances
Tmc9660AdcWrapper Thin BaseAdc adapter for AdcManager ownership

Member Typedef Documentation

◆ ConstDriverVariant

Const version of DriverVariant.

◆ DriverVariant

using Tmc9660Handler::DriverVariant = std::variant<std::monostate, SpiDriver*, UartDriver*>

Non-owning variant holding either driver type or empty (monostate).

◆ SpiDriver

TMC9660 driver instantiated with SPI communication.

◆ UartDriver

TMC9660 driver instantiated with UART communication.

Constructor & Destructor Documentation

◆ Tmc9660Handler() [1/4]

Tmc9660Handler::Tmc9660Handler ( BaseSpi & spi,
BaseGpio & rst,
BaseGpio & drv_en,
BaseGpio & faultn,
BaseGpio & wake,
uint8_t address = 0,
const tmc9660::BootloaderConfig * bootCfg = &kDefaultBootConfig )

Construct a handler for a TMC9660 connected via SPI.

The communication adapter (HalSpiTmc9660Comm) is created immediately, but the typed TMC9660 driver instance is deferred until Initialize() is called.

Parameters
spiReference to an initialized BaseSpi bus connected to the TMC9660. Must use SPI Mode 3 (CPOL=1, CPHA=1), MSB-first.
rstBaseGpio for the TMC9660 RST control pin (host-side output). Must be pre-configured with correct pin number and output direction.
drv_enBaseGpio for the TMC9660 DRV_EN control pin (host-side output).
faultnBaseGpio for the TMC9660 FAULTN status pin (host-side input).
wakeBaseGpio for the TMC9660 WAKE control pin (host-side output).
address7-bit device address used in TMCL communication (default: 0).
bootCfgPointer to bootloader configuration. The pointed-to struct must remain valid for the handler's lifetime. Pass nullptr or omit to use kDefaultBootConfig.
Warning
All GPIO and SPI references must outlive this handler.
Here is the call graph for this function:

◆ Tmc9660Handler() [2/4]

Tmc9660Handler::Tmc9660Handler ( BaseUart & uart,
BaseGpio & rst,
BaseGpio & drv_en,
BaseGpio & faultn,
BaseGpio & wake,
uint8_t address = 0,
const tmc9660::BootloaderConfig * bootCfg = &kDefaultBootConfig )

Construct a handler for a TMC9660 connected via UART.

Parameters
uartReference to an initialized BaseUart interface connected to the TMC9660.
rstBaseGpio for the TMC9660 RST control pin (host-side output).
drv_enBaseGpio for the TMC9660 DRV_EN control pin (host-side output).
faultnBaseGpio for the TMC9660 FAULTN status pin (host-side input).
wakeBaseGpio for the TMC9660 WAKE control pin (host-side output).
address7-bit device address used in TMCL communication (default: 0).
bootCfgPointer to bootloader configuration (default: kDefaultBootConfig).
Warning
All GPIO and UART references must outlive this handler.
Here is the call graph for this function:

◆ ~Tmc9660Handler()

Tmc9660Handler::~Tmc9660Handler ( )
defaultnoexcept

Destructor. Releases the driver, communication adapter, and all wrappers.

◆ Tmc9660Handler() [3/4]

Tmc9660Handler::Tmc9660Handler ( const Tmc9660Handler & )
delete

Non-copyable.

◆ Tmc9660Handler() [4/4]

Tmc9660Handler::Tmc9660Handler ( Tmc9660Handler && )
delete

Non-movable.

Member Function Documentation

◆ adc()

Tmc9660Handler::Adc & Tmc9660Handler::adc ( )

Get a reference to the ADC wrapper.

Returns
Reference to the internal Adc instance.
Warning
The returned reference is valid only as long as this handler exists.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ bootConfig()

const tmc9660::BootloaderConfig & Tmc9660Handler::bootConfig ( ) const
inlinenoexcept

Get the bootloader configuration in use.

Returns
Const reference to the BootloaderConfig struct.

◆ driverViaSpi() [1/2]

const Tmc9660Handler::SpiDriver * Tmc9660Handler::driverViaSpi ( ) const
noexcept

Const version of driverViaSpi().

Here is the call graph for this function:

◆ driverViaSpi() [2/2]

Tmc9660Handler::SpiDriver * Tmc9660Handler::driverViaSpi ( )
noexcept

Get the TMC9660 driver instance connected via SPI (nullptr if using UART).

Returns a direct pointer to the typed TMC9660<HalSpiTmc9660Comm> driver, giving full access to all 22+ subsystems (motorConfig, feedbackSense, velocityControl, protection, gateDriver, etc.) without lambda wrappers.

The user always knows which comm mode they chose at construction time, so they can safely call the matching accessor.

Returns
Pointer to the TMC9660 driver (SPI comm), or nullptr if using UART mode or Initialize() has not been called.
// Direct subsystem access after Initialize()
auto* drv = handler->driverViaSpi();
drv->feedbackSense.configureHall();
drv->motorConfig.setType(tmcl::MotorType::BLDC_MOTOR, 7);
drv->velocityControl.setTargetVelocity(1000);
drv->protection.configureVoltage(48000, 10000);
Warning
Raw pointer — NOT mutex-protected. Caller is responsible for external synchronization in multi-task environments. Prefer visitDriver() for thread-safe access.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ driverViaUart() [1/2]

const Tmc9660Handler::UartDriver * Tmc9660Handler::driverViaUart ( ) const
noexcept

Const version of driverViaUart().

Here is the call graph for this function:

◆ driverViaUart() [2/2]

Tmc9660Handler::UartDriver * Tmc9660Handler::driverViaUart ( )
noexcept

Get the TMC9660 driver instance connected via UART (nullptr if using SPI).

Returns
Pointer to the TMC9660 driver (UART comm), or nullptr if using SPI mode or Initialize() has not been called.
auto* drv = handler->driverViaUart();
drv->feedbackSense.configureABNEncoder(1024);
Warning
Raw pointer — NOT mutex-protected. Prefer visitDriver().
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DumpDiagnostics()

void Tmc9660Handler::DumpDiagnostics ( )
noexcept

Dump comprehensive diagnostics to the system log.

Logs device status, communication mode, supply voltage, chip temperature, status/error flags, GPIO wrapper count, ADC status, and bootloader config summary at INFO level.

Note
Non-const because telemetry reads require I2C transactions.
Here is the call graph for this function:

◆ EnsureInitialized()

bool Tmc9660Handler::EnsureInitialized ( )
noexcept

Ensure driver is initialized (lazy init entrypoint).

Returns
true if initialized and ready.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EnsureInitializedLocked()

bool Tmc9660Handler::EnsureInitializedLocked ( )
privatenoexcept

Ensure initialized while handler_mutex_ is already held.

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

◆ GetCommMode()

tmc9660::CommMode Tmc9660Handler::GetCommMode ( ) const
noexcept

Get the communication mode in use (SPI or UART).

Returns
tmc9660::CommMode::SPI or tmc9660::CommMode::UART.

◆ GetDescription()

const char * Tmc9660Handler::GetDescription ( ) const
noexcept

Get a human-readable description of the handler.

Returns
String e.g. "TMC9660 Motor Driver (SPI @0x01)"

◆ GetDriver() [1/2]

ConstDriverVariant Tmc9660Handler::GetDriver ( ) const
inlinenoexcept

Const overload of GetDriver().

Here is the call graph for this function:

◆ GetDriver() [2/2]

DriverVariant Tmc9660Handler::GetDriver ( )
inlinenoexcept

Get the active driver pointer without requiring visitor usage.

Returns
SpiDriver* or UartDriver* wrapped in std::variant, or monostate if unavailable.
Here is the call graph for this function:

◆ gpio()

Tmc9660Handler::Gpio & Tmc9660Handler::gpio ( uint8_t gpioNumber)

Get a reference to the GPIO wrapper for a TMC9660 internal GPIO pin.

Parameters
gpioNumberTMC9660 internal GPIO pin number. Currently supported: 17, 18.
Returns
Reference to the corresponding Gpio wrapper instance.
Note
If an unsupported GPIO number is given, falls back to GPIO17's wrapper.
Warning
The returned reference is valid only as long as this handler exists.
Here is the call graph for this function:

◆ Initialize()

bool Tmc9660Handler::Initialize ( bool performReset = true,
bool retrieveBootloaderInfo = true,
bool failOnVerifyError = false )

Initialize the TMC9660 device.

Performs the following sequence:

  1. Creates the typed TMC9660 driver instance (SpiDriver or UartDriver).
  2. Executes the bootloader initialization sequence via the driver's bootloaderInit() method, which:
    • Optionally performs a hardware reset via the RST pin
    • Writes the bootloader configuration words
    • Optionally verifies each configuration word readback
    • Enters parameter mode for TMCL operation
  3. Creates the GPIO, ADC, and Temperature peripheral wrappers.
Parameters
performResetIf true, assert RST pin to perform a hardware reset before configuration (default: true).
retrieveBootloaderInfoIf true, read the bootloader firmware version and log it (default: true).
failOnVerifyErrorIf true, return false if any bootloader config word fails readback verification. If false, log a warning but continue (default: false).
Returns
true if initialization completed successfully, false on any failure.
Note
Can be called multiple times; subsequent calls skip driver creation if already initialized.
Warning
This method must be called before any motor control, telemetry, or peripheral access methods.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ IsDriverReady()

bool Tmc9660Handler::IsDriverReady ( ) const
noexcept

Check if the TMC9660 driver has been initialized and is ready.

Returns
true if Initialize() has completed successfully and the driver instance exists, false otherwise.
Here is the caller graph for this function:

◆ operator=() [1/2]

◆ operator=() [2/2]

◆ temperature()

Tmc9660Handler::Temperature & Tmc9660Handler::temperature ( )

Get a reference to the Temperature wrapper.

Returns
Reference to the internal Temperature instance.
Warning
The returned reference is valid only as long as this handler exists.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ visitDriver() [1/2]

template<typename Func >
auto Tmc9660Handler::visitDriver ( Func && func) const
inlinenoexcept

Const overload of visitDriver().

Here is the call graph for this function:

◆ visitDriver() [2/2]

template<typename Func >
auto Tmc9660Handler::visitDriver ( Func && func)
inlinenoexcept

Execute a function on the underlying typed TMC9660 driver.

For most use cases, prefer driverViaSpi() or driverViaUart() for direct typed access. Use visitDriver() only when writing generic code that must work with either communication mode (e.g., in manager-level code that doesn't know the comm type).

Template Parameters
FuncCallable type with signature ReturnType(auto& driver).
Parameters
funcThe visitor function to execute.
Returns
The return value of func, or a default-constructed value if the driver is not ready.
Here is the call graph for this function:

◆ visitDriverInternal()

template<typename Func >
auto Tmc9660Handler::visitDriverInternal ( Func && func)
inlineprivatenoexcept

Internal no-lock visitDriver for use from already-locked contexts.

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

Member Data Documentation

◆ adcWrapper_

std::unique_ptr<Adc> Tmc9660Handler::adcWrapper_
private

Multi-channel ADC wrapper.

◆ bootCfg_

const tmc9660::BootloaderConfig* Tmc9660Handler::bootCfg_
private

Bootloader config (not owned; must outlive handler).

◆ description_

char Tmc9660Handler::description_[64] {}
private

Human-readable handler description.

◆ device_address_

uint8_t Tmc9660Handler::device_address_
private

7-bit TMCL device address.

◆ gpioWrappers_

std::array<std::unique_ptr<Gpio>, 2> Tmc9660Handler::gpioWrappers_
private

GPIO17 [0] and GPIO18 [1].

◆ handler_mutex_

RtosMutex Tmc9660Handler::handler_mutex_
mutableprivate

Recursive mutex for thread-safe access.

◆ kDefaultBootConfig

const tmc9660::BootloaderConfig Tmc9660Handler::kDefaultBootConfig
static

Default bootloader configuration based on TMC9660-3PH-EVAL board settings.

Default TMC9660 bootloader configuration based on TMC9660-3PH-EVAL board settings.

Configures:

  • LDO: VEXT1=5.0V, VEXT2=3.3V, 3ms slope
  • Boot mode: Parameter mode with motor control start
  • UART: Auto16x baud rate, GPIO6/7, address 1
  • External clock: 16MHz crystal with PLL (40MHz system)
  • SPI Flash: Enabled on SPI0 (GPIO11/12) at 10MHz
  • GPIO: GPIO5 analog, GPIO17/18 digital pull-down
  • Hall, ABN encoders, step/dir, etc.: disabled (safe defaults)

Override by passing a custom tmc9660::BootloaderConfig* to the constructor.

Configuration highlights:

  • LDO: VEXT1=5.0V, VEXT2=3.3V with 3ms slope control
  • Boot Mode: Parameter mode for TMCL parameter-based control
  • UART: Auto16x baud rate detection, GPIO6/7 pins, device address 1
  • External Clock: 16MHz crystal with PLL for stable 40MHz system clock
  • SPI Flash: Enabled on SPI0 (GPIO11 SCK, GPIO12 CS) at 10MHz
  • GPIO: GPIO5 analog input, GPIO17/18 digital inputs with pull-down
  • New sections (hall, abn1, abn2, ref, stepDir, spiEnc, mechBrake, brakeChopper, memStorage) use safe defaults (disabled)

◆ spi_comm_

std::unique_ptr<HalSpiTmc9660Comm> Tmc9660Handler::spi_comm_
private

SPI communication adapter (or nullptr).

◆ spi_driver_

std::unique_ptr<SpiDriver> Tmc9660Handler::spi_driver_
private

TMC9660 driver for SPI mode (or nullptr).

◆ temperatureWrapper_

std::unique_ptr<Temperature> Tmc9660Handler::temperatureWrapper_
private

Chip temperature wrapper.

◆ uart_comm_

std::unique_ptr<HalUartTmc9660Comm> Tmc9660Handler::uart_comm_
private

UART communication adapter (or nullptr).

◆ uart_driver_

std::unique_ptr<UartDriver> Tmc9660Handler::uart_driver_
private

TMC9660 driver for UART mode (or nullptr).

◆ use_spi_

bool Tmc9660Handler::use_spi_
private

true = SPI communication active, false = UART.


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