TMC5160 Driver
API reference
Loading...
Searching...
No Matches
Public Member Functions | Static Public Attributes | Protected Member Functions | Protected Attributes | Private Member Functions | Private Attributes | List of all members
tmc51x0::SpiCommInterface< Derived > Class Template Reference

CRTP-based SPI implementation of TMC5160CommInterface. More...

#include <tmc51x0_comm_interface.hpp>

Inheritance diagram for tmc51x0::SpiCommInterface< Derived >:
[legend]
Collaboration diagram for tmc51x0::SpiCommInterface< Derived >:
[legend]

Public Member Functions

 SpiCommInterface () noexcept
 Construct SPI communication interface.
 
void SetDaisyChainLength (uint8_t total_length) noexcept
 Set the total number of devices in the daisy chain.
 
uint8_t GetDaisyChainLength () const noexcept
 Get the total number of devices in the daisy chain.
 
uint8_t AutoDetectChainLength (uint8_t max_devices=8) noexcept
 Auto-detect the daisy chain length by sending a unique command that loops back.
 
CommMode GetMode () const noexcept
 Get communication mode (always SPI for this interface)
 
Result< void > SpiTransfer (const uint8_t *tx, uint8_t *rx, size_t length) noexcept
 Low-level SPI transfer for register read/write.
 
Result< uint32_t > ReadRegister (uint8_t address, uint8_t daisy_chain_position=0) noexcept
 Read a 32-bit register via SPI.
 
Result< void > WriteRegister (uint8_t address, uint32_t value, uint8_t daisy_chain_position=0) noexcept
 Write a 32-bit register via SPI.
 
 SpiCommInterface (const SpiCommInterface &)=delete
 
SpiCommInterface & operator= (const SpiCommInterface &)=delete
 
- Public Member Functions inherited from tmc51x0::CommInterface< Derived >
 CommInterface () noexcept=default
 Construct communication interface.
 
CommMode GetMode () const noexcept
 Get the underlying communication mode used by this interface.
 
Result< uint32_t > ReadRegister (uint8_t address, uint8_t address_param=0) noexcept
 Read a 32-bit register from the TMC5160.
 
Result< void > WriteRegister (uint8_t address, uint32_t value, uint8_t address_param=0) noexcept
 Write a 32-bit register to the TMC5160.
 
Result< void > GpioSet (TMC51x0CtrlPin pin, GpioSignal signal) noexcept
 Set GPIO pin signal state (output control)
 
Result< GpioSignal > GpioRead (TMC51x0CtrlPin pin) noexcept
 Read GPIO pin signal state (input state)
 
Result< void > GpioSetActive (TMC51x0CtrlPin pin) noexcept
 Set GPIO pin to active state (convenience method)
 
Result< void > GpioSetInactive (TMC51x0CtrlPin pin) noexcept
 Set GPIO pin to inactive state (convenience method)
 
void DelayMs (uint32_t ms) noexcept
 Delay execution for specified milliseconds.
 
void DelayUs (uint32_t us) noexcept
 Delay execution for specified microseconds.
 
Result< void > SetPowerEnabled (bool enabled) noexcept
 Enable/disable power to the TMC51x0 (optional)
 
Result< void > PowerCycle (uint32_t power_off_ms=20, uint32_t power_on_settle_ms=20) noexcept
 Power-cycle the TMC51x0 (optional)
 
Result< void > SetClkFreq (uint32_t frequency_hz) noexcept
 Set external clock frequency on CLK pin (optional)
 
 CommInterface (const CommInterface &)=delete
 
CommInterface & operator= (const CommInterface &)=delete
 
void LogDebug (int level, const char *tag, const char *format,...) noexcept
 Public debug logging wrapper for external classes.
 
void LogDebug (LogLevel level, const char *tag, const char *format,...) noexcept
 LogDebug overload that accepts the driver-native LogLevel enum.
 

Static Public Attributes

static constexpr size_t kSpiScratchBytes
 

Protected Member Functions

 ~SpiCommInterface ()=default
 Protected destructor.
 
 SpiCommInterface (SpiCommInterface &&)=default
 
SpiCommInterface & operator= (SpiCommInterface &&)=default
 
- Protected Member Functions inherited from tmc51x0::CommInterface< Derived >
void DebugLog (int level, const char *tag, const char *format, va_list args) noexcept
 Debug logging function for detailed debugging information.
 
 ~CommInterface ()=default
 Protected destructor.
 
 CommInterface (CommInterface &&)=default
 
CommInterface & operator= (CommInterface &&)=default
 

Protected Attributes

uint8_t total_chain_length_
 Total number of devices in the daisy chain.
 
uint8_t user_specified_chain_length_ {0}
 
bool chain_length_verified_ {false}
 

Private Member Functions

Result< void > EnsureChainLengthKnown (uint8_t daisy_chain_position, const char *context) noexcept
 Ensure chain length is known and verified for daisy-chain operations.
 

Private Attributes

std::array< uint8_t, kSpiScratchBytes > tx_scratch_ {}
 
std::array< uint8_t, kSpiScratchBytes > rx_scratch_ {}
 
uint32_t drv_err_log_count_ {0}
 

Detailed Description

template<typename Derived>
class tmc51x0::SpiCommInterface< Derived >

CRTP-based SPI implementation of TMC5160CommInterface.

Uses a 4-wire SPI bus (mode 3) to exchange 40-bit datagrams per datasheet section 4.1. The implementation uses 8 bytes (64 bits) for convenience, which is acceptable per datasheet as additional bits beyond 40 are shifted through an internal shift register for daisy-chaining. Data is sent MSB-first, big-endian.

SPI Datagram Structure (40 bits):

Byte structure (MSB transmitted first):

Response (40 bits):

Read access (pipelined per datasheet section 4.1.1):

Write access:

SPI Signals (per datasheet section 4.2):

Timing (per datasheet section 4.3):

Daisy-Chaining Support

The TMC5160 supports daisy-chaining multiple chips on a single SPI bus per datasheet section 4.2. Each chip has an internal 40-bit shift register. Data on SDI is continuously shifted out on SDO with a 40-clock cycle delay, allowing data to propagate through the chain.

Hardware Setup for Daisy-Chaining:

How Daisy-Chaining Works:

Response Reading (per datasheet):

CRITICAL: Chain Length MUST Always Be Known

Sending vs Receiving Calculations:

Chain Length Requirements:

Auto-Detection on First Access:

Verification of User-Specified Length:

Why This Matters:

For multi-chip simultaneous communication:

Architecture:

Example usage:

class MySPI : public tmc5160::SpiCommInterface<MySPI> {
public:
CommMode mode() const noexcept { return CommMode::SPI; }
bool spiTransfer(...) { ... }
bool gpioSet(...) { ... }
bool gpioRead(...) { ... }
void debugLog(...) { ... }
void delayMs(...) { ... }
void delayUs(...) { ... }
};
CommMode
Supported physical communication modes for TMC51x0.
Definition tmc51x0_comm_interface.hpp:221
Template Parameters
DerivedThe derived class type (CRTP pattern)

Constructor & Destructor Documentation

◆ SpiCommInterface() [1/3]

template<typename Derived >
tmc51x0::SpiCommInterface< Derived >::SpiCommInterface ( )
inlinenoexcept

Construct SPI communication interface.

Note: Pin active level configuration is handled by the derived class. The base class only deals with abstract signals (ACTIVE/INACTIVE).

◆ ~SpiCommInterface()

template<typename Derived >
tmc51x0::SpiCommInterface< Derived >::~SpiCommInterface ( )
protecteddefault

Protected destructor.

◆ SpiCommInterface() [2/3]

template<typename Derived >
tmc51x0::SpiCommInterface< Derived >::SpiCommInterface ( SpiCommInterface< Derived > &&  )
protecteddefault

◆ SpiCommInterface() [3/3]

template<typename Derived >
tmc51x0::SpiCommInterface< Derived >::SpiCommInterface ( const SpiCommInterface< Derived > &  )
delete

Member Function Documentation

◆ AutoDetectChainLength()

template<typename Derived >
uint8_t tmc51x0::SpiCommInterface< Derived >::AutoDetectChainLength ( uint8_t  max_devices = 8)
inlinenoexcept

Auto-detect the daisy chain length by sending a unique command that loops back.

This method sends a command with a unique, recognizable pattern to position (max_devices+1), which is beyond the last device. The command will shift through all devices and loop back to the MCU via the last device's SDO. By searching for our exact command pattern in the received data, we can determine the actual chain length.

Algorithm:

  1. Create a unique command with a distinctive pattern (e.g., read register 0x73 with unique data)
  2. Send command to position (max_devices+1) - beyond the last device
  3. Send enough padding: (max_devices+2)*40 bits total to ensure loopback is captured
  4. The command loops back after (n+1)*40 bits where n is the actual chain length
  5. Search received data for our exact command pattern
  6. When found at offset (n+1)*5, chain length = n
Parameters
max_devicesMaximum number of devices to probe (default: 8, max: 255)
Returns
Detected chain length (0 = single chip or detection failed, >0 = number of devices)
Note
This method requires that devices are powered (but don't need to be initialized)
The command uses a read operation which is safe and doesn't modify device state
The detected length is automatically set via SetDaisyChainLength()
Warning
This method performs a full SPI transaction and may take time
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EnsureChainLengthKnown()

template<typename Derived >
Result< void > tmc51x0::SpiCommInterface< Derived >::EnsureChainLengthKnown ( uint8_t  daisy_chain_position,
const char *  context 
)
inlineprivatenoexcept

Ensure chain length is known and verified for daisy-chain operations.

Parameters
daisy_chain_positionPosition in daisy chain (0 = first chip/single chip)
contextContext string for logging (e.g., "ReadRegister", "WriteRegister")
Returns
Result<void> indicating success or error

This function handles:

  • Auto-detection if daisy_chain_position > 0 and chain length is unknown
  • Setting chain length to 1 for single chip mode (daisy_chain_position == 0)
  • Verification of user-specified chain length against auto-detected value
  • Verification of set chain length against auto-detected value
Note
This function is defined in the class body, so it's implicitly inline. This allows compiler optimization while maintaining code clarity and avoiding duplication between ReadRegister and WriteRegister.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetDaisyChainLength()

template<typename Derived >
uint8_t tmc51x0::SpiCommInterface< Derived >::GetDaisyChainLength ( ) const
inlinenoexcept

Get the total number of devices in the daisy chain.

Returns
Total chain length (0 = unknown/single chip, >0 = total number of devices)

◆ GetMode()

template<typename Derived >
CommMode tmc51x0::SpiCommInterface< Derived >::GetMode ( ) const
inlinenoexcept

Get communication mode (always SPI for this interface)

Returns
CommMode::SPI

◆ operator=() [1/2]

template<typename Derived >
SpiCommInterface & tmc51x0::SpiCommInterface< Derived >::operator= ( const SpiCommInterface< Derived > &  )
delete

◆ operator=() [2/2]

template<typename Derived >
SpiCommInterface & tmc51x0::SpiCommInterface< Derived >::operator= ( SpiCommInterface< Derived > &&  )
protecteddefault

◆ ReadRegister()

template<typename Derived >
Result< uint32_t > tmc51x0::SpiCommInterface< Derived >::ReadRegister ( uint8_t  address,
uint8_t  daisy_chain_position = 0 
)
inlinenoexcept

Read a 32-bit register via SPI.

Parameters
addressRegister address (0x00-0x73)
valueReference to store the read value
daisy_chain_positionPosition in daisy chain (0 = first chip/single chip, default: 0) Specifies which chip in the chain to address
Returns
Result<uint32_t> containing the value or error

This method handles both single-chip and daisy-chain modes:

  • Single chip (daisy_chain_position = 0): Standard 40-bit SPI transaction (5 bytes)
  • Daisy-chain (daisy_chain_position > 0): Sends command with padding to shift it to the target device position in the chain

Daisy-Chain Transfer Logic:

CRITICAL: Chain length MUST be known for correct operation

  • Chain length is automatically detected on first access if not manually set
  • If detection fails, operation will fail (chain length is required)

Sending (Command Transmission):

  • Command is placed at bytes 0-4 (the 40-bit SPI frame)
  • Padding (zeros) is added from byte 5 onwards
  • To address device k: Send (k+1)*40 bits = (k+1)*5 bytes minimum
    • This shifts the command through k devices to reach device k

Receiving (Response Extraction):

  • To receive response from device k in chain of n devices:
    • Total transfer size: 40·(n-k) bits = (n-k)*5 bytes (datasheet formula)
    • Response appears at offset (n-k-1)*5 bytes (reverse order: last device first)
    • This requires knowing total chain length n

Transfer Size Calculation:

  • For simultaneous send/receive, use max((k+1)*5, (n-k)*5) bytes
    • Sending requirement: (k+1)*5 bytes to shift command to device k
    • Receiving requirement: (n-k)*5 bytes to shift response back (datasheet formula)
    • Use max() to ensure both requirements are met:
      • For k < n/2: (n-k)*5 >= (k+1)*5, so (n-k)*5 dominates
      • For k >= n/2: (k+1)*5 > (n-k)*5, so (k+1)*5 dominates
    • Extra bytes beyond (k+1)*5 are padding (zeros) for full-duplex behavior

Auto-Detection and Verification:

  • If daisy_chain_position > 0 and chain length is unknown (total_chain_length_ == 0), chain length is automatically detected on first access
  • If user has specified a chain length via SetDaisyChainLength(), it is verified against auto-detected length on first access
  • If mismatch is detected, an error is logged and the detected length is used
  • If detection fails, operation returns false (chain length is required)

Architecture Note: This SpiCommInterface instance can be shared by multiple TMC5160 drivers on the same SPI bus. Each TMC5160 instance has its own daisy_chain_position_ and passes it as a parameter when calling ReadRegister() or WriteRegister().

CSN is handled automatically by the SPI hardware or derived class implementation. For daisy-chaining, all chips share the same CSN (tied together).

Here is the call graph for this function:

◆ SetDaisyChainLength()

template<typename Derived >
void tmc51x0::SpiCommInterface< Derived >::SetDaisyChainLength ( uint8_t  total_length)
inlinenoexcept

Set the total number of devices in the daisy chain.

Parameters
total_lengthTotal number of devices in the chain (1 = single chip, 2 = two chips, etc.) Set to 0 to disable daisy-chain mode (single chip, default)
Note
This is CRITICAL for proper response extraction using the datasheet formula 40·(n-k)
This should be set once during initialization, before any register access
For optimal efficiency with TMC51x0DaisyChain, set this to match the chain length
If set to a non-zero value, it will be verified against auto-detected length on first access. If mismatch is detected, ReadRegister/WriteRegister will return false.
If set to 0 and daisy_chain_position > 0, chain length will be auto-detected on first access.

◆ SpiTransfer()

template<typename Derived >
Result< void > tmc51x0::SpiCommInterface< Derived >::SpiTransfer ( const uint8_t *  tx,
uint8_t *  rx,
size_t  length 
)
inlinenoexcept

Low-level SPI transfer for register read/write.

Parameters
txBuffer containing bytes to transmit
rxBuffer to receive bytes from device
lengthNumber of bytes to transfer
Returns
Result<void> indicating success or error
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteRegister()

template<typename Derived >
Result< void > tmc51x0::SpiCommInterface< Derived >::WriteRegister ( uint8_t  address,
uint32_t  value,
uint8_t  daisy_chain_position = 0 
)
inlinenoexcept

Write a 32-bit register via SPI.

Parameters
addressRegister address (0x00-0x73)
value32-bit value to write
daisy_chain_positionPosition in daisy chain (0 = first chip/single chip, default: 0) Specifies which chip in the chain to address
Returns
Result<void> indicating success or error

This method handles both single-chip and daisy-chain modes:

  • Single chip (daisy_chain_position = 0): Standard 40-bit SPI transaction (5 bytes)
  • Daisy-chain (daisy_chain_position > 0): Sends command with padding to shift it to the target device position in the chain

Daisy-Chain Transfer Logic: Same as ReadRegister() - see ReadRegister() documentation for detailed explanation.

Key Points:

  • Chain length MUST be known (auto-detected if not set)
  • Sending: (k+1)*5 bytes to address device k
  • Receiving: (n-k)*5 bytes total, response at offset (n-k-1)*5 bytes
  • Transfer size: max((k+1)*5, (n-k)*5) bytes
  • Extra bytes beyond (k+1)*5 are padding (zeros) for full-duplex behavior

Auto-Detection and Verification: Same as ReadRegister() - chain length is auto-detected on first access if needed, and user-specified length is verified against detected length. If detection fails, operation returns false.

Architecture Note: This SpiCommInterface instance can be shared by multiple TMC5160 drivers on the same SPI bus. Each TMC5160 instance has its own daisy_chain_position_ and passes it as a parameter when calling ReadRegister() or WriteRegister().

CSN is handled automatically by the SPI hardware or derived class implementation. For daisy-chaining, all chips share the same CSN (tied together).

Here is the call graph for this function:

Member Data Documentation

◆ chain_length_verified_

template<typename Derived >
bool tmc51x0::SpiCommInterface< Derived >::chain_length_verified_ {false}
protected

Flag to track if chain length has been verified via auto-detection

◆ drv_err_log_count_

template<typename Derived >
uint32_t tmc51x0::SpiCommInterface< Derived >::drv_err_log_count_ {0}
private

◆ kSpiScratchBytes

template<typename Derived >
constexpr size_t tmc51x0::SpiCommInterface< Derived >::kSpiScratchBytes
staticconstexpr
Initial value:
=
static_cast<size_t>(TMC51X0_SPI_MAX_CHAIN_DEVICES + 2U) * 5U
#define TMC51X0_SPI_MAX_CHAIN_DEVICES
Definition tmc51x0_comm_interface.hpp:1672

◆ rx_scratch_

template<typename Derived >
std::array<uint8_t, kSpiScratchBytes> tmc51x0::SpiCommInterface< Derived >::rx_scratch_ {}
private

◆ total_chain_length_

template<typename Derived >
uint8_t tmc51x0::SpiCommInterface< Derived >::total_chain_length_
protected
Initial value:
{
0}

Total number of devices in the daisy chain.

0 = unknown/single chip (uses simplified approach) >0 = total number of devices (uses datasheet formula 40·(n-k))

This is CRITICAL for proper response extraction. When set correctly, ReadRegister and WriteRegister use the optimal datasheet formula. Total number of devices in daisy chain (0 = unknown/single chip)

◆ tx_scratch_

template<typename Derived >
std::array<uint8_t, kSpiScratchBytes> tmc51x0::SpiCommInterface< Derived >::tx_scratch_ {}
private

◆ user_specified_chain_length_

template<typename Derived >
uint8_t tmc51x0::SpiCommInterface< Derived >::user_specified_chain_length_ {0}
protected

User-specified chain length (0 = not specified, >0 = user value)


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