99#ifndef TMC9660_DISABLE_DEBUG_LOGGING
101#define TMC9660_LOG_DEBUG(comm_obj, level, tag, ...) (comm_obj).logDebug(level, tag, __VA_ARGS__)
104#define TMC9660_LOG_DEBUG(comm_obj, level, tag, ...) ((void)0)
170static constexpr uint8_t
tmclChecksum(
const uint8_t* bytes,
size_t n, uint8_t start = 0) noexcept {
172 for (
size_t i = start; i < n; ++i)
199 [[nodiscard]]
bool isOK() const noexcept {
214 return (
static_cast<uint32_t
>(
rawBytes[offset]) << 24) |
215 (
static_cast<uint32_t
>(
rawBytes[offset + 1]) << 16) |
216 (
static_cast<uint32_t
>(
rawBytes[offset + 2]) << 8) |
217 static_cast<uint32_t
>(
rawBytes[offset + 3]);
233 for (
size_t i = 1; i < 8; ++i) {
235 version +=
static_cast<char>(
rawBytes[i]);
260 uint16_t sent_type = 0) noexcept {
262 std::copy(in.begin(), in.end(), r.
rawBytes.begin());
265 uint8_t raw_status = in[0];
266 if (raw_status == 0x13) {
273 r.
value = (
static_cast<uint32_t
>(in[3]) << 24) | (
static_cast<uint32_t
>(in[4]) << 16) |
274 (
static_cast<uint32_t
>(in[5]) << 8) |
static_cast<uint32_t
>(in[6]);
281 if (sent_opcode == 136 && sent_type == 0) {
298 r.
value = (
static_cast<uint32_t
>(in[3]) << 24) | (
static_cast<uint32_t
>(in[4]) << 16) |
299 (
static_cast<uint32_t
>(in[5]) << 8) |
static_cast<uint32_t
>(in[6]);
321 uint8_t sent_opcode = 0, uint16_t sent_type = 0) noexcept {
323 std::copy(in.begin() + 1, in.end(), r.
rawBytes.begin());
336 if (sent_opcode == 136 && sent_type == 0) {
348 if ((in[1] & 0xFE) != (addr & 0xFE))
358 r.
value = (
static_cast<uint32_t
>(in[4]) << 24) | (
static_cast<uint32_t
>(in[5]) << 16) |
359 (
static_cast<uint32_t
>(in[6]) << 8) |
static_cast<uint32_t
>(in[7]);
379 void toSpi(std::span<uint8_t, 8> out)
const noexcept {
381 out[1] =
static_cast<uint8_t
>(
type & 0xFF);
383 out[2] =
static_cast<uint8_t
>(((
motor & 0x0F) << 4) | ((
type & 0xF00) >> 8));
384 out[3] =
static_cast<uint8_t
>(
value >> 24);
385 out[4] =
static_cast<uint8_t
>(
value >> 16);
386 out[5] =
static_cast<uint8_t
>(
value >> 8);
387 out[6] =
static_cast<uint8_t
>(
value);
396 void toUart(uint8_t addr, std::span<uint8_t, 9> out)
const noexcept {
401 out[0] = (addr & 0xFE) | 0x01;
403 out[2] =
static_cast<uint8_t
>(
type & 0xFF);
404 out[3] =
static_cast<uint8_t
>(
motor & 0x0F);
405 out[4] =
static_cast<uint8_t
>(
value >> 24);
406 out[5] =
static_cast<uint8_t
>(
value >> 16);
407 out[6] =
static_cast<uint8_t
>(
value >> 8);
408 out[7] =
static_cast<uint8_t
>(
value);
422 f.
type =
static_cast<uint16_t
>(in[1] | ((in[2] & 0x0F) << 8));
423 f.
motor = (in[2] >> 4) & 0x0F;
424 f.
value = (
static_cast<uint32_t
>(in[3]) << 24) | (
static_cast<uint32_t
>(in[4]) << 16) |
425 (
static_cast<uint32_t
>(in[5]) << 8) |
static_cast<uint32_t
>(in[6]);
439 out_frame.
opcode = reply.opcode;
440 out_frame.value = reply.value;
451 static bool fromUart(std::span<const uint8_t, 9> in, uint8_t expected_addr,
456 out_frame.
opcode = rep.opcode;
457 out_frame.value = rep.value;
469 for (
size_t i = 0; i < n; ++i)
513template <
typename Derived>
523 CommInterface(
bool rst_active_level,
bool drv_en_active_level,
bool wake_active_level,
524 bool faultn_active_level) noexcept
525 :
pinActiveLevels_{rst_active_level, drv_en_active_level, wake_active_level, faultn_active_level} {}
537 return static_cast<const Derived*
>(
this)->
mode();
558 const TMCLFrame* second_command =
nullptr) noexcept {
559 return static_cast<Derived*
>(
this)->
transferTMCL(tx, reply, address, first_reply, second_command);
574 return static_cast<Derived*
>(
this)->
gpioSet(pin, signal);
589 return static_cast<Derived*
>(
this)->
gpioRead(pin, signal);
737 void debugLog(
int level,
const char* tag,
const char* format, va_list args)
noexcept {
738 static_cast<Derived*
>(
this)->
debugLog(level, tag, format, args);
749 static_cast<Derived*
>(
this)->
delayMs(ms);
761 static_cast<Derived*
>(
this)->
delayUs(us);
794#ifndef TMC9660_DISABLE_DEBUG_LOGGING
795 void logDebug(
int level,
const char* tag,
const char* format, ...) noexcept {
797 va_start(args, format);
800 size_t format_len = strlen(format);
801 const char* final_format = format;
802 char* modified_format =
nullptr;
804 if (format_len == 0 || format[format_len - 1] !=
'\n') {
806 modified_format =
new char[format_len + 2];
807 strcpy(modified_format, format);
808 strcat(modified_format,
"\n");
809 final_format = modified_format;
812 debugLog(level, tag, final_format, args);
814 if (modified_format) {
815 delete[] modified_format;
822 inline void logDebug(
int ,
const char* ,
const char* , ...) noexcept {
854template <
typename Derived>
865 bool faultn_active_level) noexcept
930 TMC9660_LOG_DEBUG(*
static_cast<Derived*
>(
this), 2,
"SPI_TMCL",
"[TMCL TX 1 ] %02X %02X %02X %02X %02X %02X %02X %02X",
938 TMC9660_LOG_DEBUG(*
static_cast<Derived*
>(
this), 2,
"SPI_TMCL",
"[TMCL RX 1 ] %02X %02X %02X %02X %02X %02X %02X %02X",
946 *
static_cast<Derived*
>(
this), 2,
"SPI_TMCL",
947 " └─> SPI_Status=0x%02X, TMCL_Status=0x%02X, Value=0x%08X (parse %s)",
963 "[TMCL TX 2 ] %02X %02X %02X %02X %02X %02X %02X %02X%s",
tx_buf[0],
971 "[TMCL RX 2 ] %02X %02X %02X %02X %02X %02X %02X %02X",
rx_buf[0],
rx_buf[1],
982 *
static_cast<Derived*
>(
this), 2,
"SPI_TMCL",
983 "⚠️ SPI_STATUS_NOT_READY received, retrying (attempt %u/%u) after %u us",
retry_count,
990 "❌ SPI_STATUS_NOT_READY: Max retries (%u) exceeded",
991 this->spiRetryMaxCount_);
1065template <
typename Derived>
1150 std::array<uint8_t, 9>
frame;
SPI status codes as per TMC9660 Parameter Mode.
Definition tmc9660_comm_interface.hpp:514
bool set_pin_active_level(TMC9660CtrlPin pin, bool active_level) noexcept
Configure the active level for a specific pin.
Definition tmc9660_comm_interface.hpp:645
void logDebug(int level, const char *tag, const char *format,...) noexcept
Public debug logging wrapper for external classes.
Definition tmc9660_comm_interface.hpp:795
uint8_t spiRetryMaxCount_
Maximum number of retries for SPI_STATUS_NOT_READY responses.
Definition tmc9660_comm_interface.hpp:714
bool signalToGpioLevel(TMC9660CtrlPin pin, GpioSignal signal) const noexcept
Convert signal state to physical GPIO level.
Definition tmc9660_comm_interface.hpp:598
GpioSignal gpioLevelToSignal(TMC9660CtrlPin pin, bool gpio_level) const noexcept
Convert physical GPIO level to signal state.
Definition tmc9660_comm_interface.hpp:609
~CommInterface()=default
Protected destructor.
CommInterface(const CommInterface &)=delete
bool gpioSetActive(TMC9660CtrlPin pin) noexcept
Set GPIO pin to active state (convenience method).
Definition tmc9660_comm_interface.hpp:620
CommInterface(bool rst_active_level, bool drv_en_active_level, bool wake_active_level, bool faultn_active_level) noexcept
Construct communication interface with pin active level configuration.
Definition tmc9660_comm_interface.hpp:523
void setSpiRetryInterval(uint32_t interval_us) noexcept
Set delay interval between retry attempts for SPI_STATUS_NOT_READY.
Definition tmc9660_comm_interface.hpp:680
void setSpiRetryMaxCount(uint8_t max_retries) noexcept
Set maximum number of retries for SPI_STATUS_NOT_READY responses.
Definition tmc9660_comm_interface.hpp:659
uint32_t getSpiRetryInterval() const noexcept
Get current retry interval for SPI_STATUS_NOT_READY.
Definition tmc9660_comm_interface.hpp:688
CommInterface & operator=(CommInterface &&)=default
bool transferTMCL(const TMCLFrame &tx, TMCLReply &reply, uint8_t address, TMCLReply *first_reply=nullptr, const TMCLFrame *second_command=nullptr) noexcept
Perform a full duplex TMCL transfer for parameter mode communication.
Definition tmc9660_comm_interface.hpp:556
CommInterface(CommInterface &&)=default
uint32_t spiRetryIntervalUs_
Delay interval in microseconds between retry attempts.
Definition tmc9660_comm_interface.hpp:723
bool gpioRead(TMC9660CtrlPin pin, GpioSignal &signal) noexcept
Read GPIO pin signal state (input state).
Definition tmc9660_comm_interface.hpp:588
uint8_t getSpiRetryMaxCount() const noexcept
Get current maximum retry count for SPI_STATUS_NOT_READY.
Definition tmc9660_comm_interface.hpp:667
void delayUs(uint32_t us) noexcept
Delay execution for specified microseconds.
Definition tmc9660_comm_interface.hpp:760
bool gpioSetInactive(TMC9660CtrlPin pin) noexcept
Set GPIO pin to inactive state (convenience method).
Definition tmc9660_comm_interface.hpp:630
void debugLog(int level, const char *tag, const char *format, va_list args) noexcept
Debug logging function for detailed debugging information.
Definition tmc9660_comm_interface.hpp:737
bool pinActiveLevels_[4]
Pin active level configuration storage.
Definition tmc9660_comm_interface.hpp:705
CommInterface & operator=(const CommInterface &)=delete
void delayMs(uint32_t ms) noexcept
Delay execution for specified milliseconds.
Definition tmc9660_comm_interface.hpp:748
CommMode mode() const noexcept
Get the underlying communication mode used by this interface.
Definition tmc9660_comm_interface.hpp:536
bool gpioSet(TMC9660CtrlPin pin, GpioSignal signal) noexcept
Set GPIO pin signal state (output control).
Definition tmc9660_comm_interface.hpp:573
CRTP-based SPI implementation of TMC9660CommInterface.
Definition tmc9660_comm_interface.hpp:855
bool spiTransferBootloader(std::array< uint8_t, 5 > &tx, std::array< uint8_t, 5 > &rx) noexcept
Low-level SPI transfer for bootloader communication.
Definition tmc9660_comm_interface.hpp:900
SpiCommInterface & operator=(const SpiCommInterface &)=delete
bool spiTransferTMCL(std::array< uint8_t, 8 > &tx, std::array< uint8_t, 8 > &rx) noexcept
Low-level SPI transfer for TMCL parameter mode communication.
Definition tmc9660_comm_interface.hpp:884
CommMode mode() const noexcept
Get communication mode (always SPI for this interface)
Definition tmc9660_comm_interface.hpp:873
bool transferTMCL(const TMCLFrame &tx, TMCLReply &reply, uint8_t, TMCLReply *first_reply, const TMCLFrame *second_command) noexcept
Definition tmc9660_comm_interface.hpp:924
SpiCommInterface(bool rst_active_level, bool drv_en_active_level, bool wake_active_level, bool faultn_active_level) noexcept
Construct SPI communication interface with pin active level configuration.
Definition tmc9660_comm_interface.hpp:864
SpiCommInterface(const SpiCommInterface &)=delete
SpiCommInterface & operator=(SpiCommInterface &&)=default
~SpiCommInterface()=default
Protected destructor.
bool gpioSet(TMC9660CtrlPin pin, GpioSignal signal) noexcept
Set GPIO pin signal state for SPI interface.
Definition tmc9660_comm_interface.hpp:910
SpiCommInterface(SpiCommInterface &&)=default
bool gpioRead(TMC9660CtrlPin pin, GpioSignal &signal) noexcept
Read GPIO pin signal state for SPI interface.
Definition tmc9660_comm_interface.hpp:920
CRTP-based UART implementation of TMC9660CommInterface.
Definition tmc9660_comm_interface.hpp:1066
bool uartReceiveTMCL(std::array< uint8_t, 9 > &data) noexcept
Receive raw 9-byte UART TMCL datagram for parameter mode communication.
Definition tmc9660_comm_interface.hpp:1104
CommMode mode() const noexcept
Get communication mode (always UART for this interface)
Definition tmc9660_comm_interface.hpp:1084
bool gpioSet(TMC9660CtrlPin pin, GpioSignal signal) noexcept
Set GPIO pin signal state for UART interface.
Definition tmc9660_comm_interface.hpp:1130
UartCommInterface(const UartCommInterface &)=delete
UartCommInterface(UartCommInterface &&)=default
bool gpioRead(TMC9660CtrlPin pin, GpioSignal &signal) noexcept
Read GPIO pin signal state for UART interface.
Definition tmc9660_comm_interface.hpp:1140
~UartCommInterface()=default
Protected destructor.
UartCommInterface & operator=(const UartCommInterface &)=delete
bool uartSendTMCL(const std::array< uint8_t, 9 > &data) noexcept
Send raw 9-byte UART TMCL datagram for parameter mode communication.
Definition tmc9660_comm_interface.hpp:1094
bool transferTMCL(const TMCLFrame &tx, TMCLReply &reply, uint8_t address, TMCLReply *first_reply, const TMCLFrame *second_command) noexcept
Definition tmc9660_comm_interface.hpp:1144
UartCommInterface & operator=(UartCommInterface &&)=default
bool uartTransferBootloader(const std::array< uint8_t, 8 > &tx, std::array< uint8_t, 8 > &rx) noexcept
Transfer 8-byte UART bootloader datagram (send and receive).
Definition tmc9660_comm_interface.hpp:1120
UartCommInterface(bool rst_active_level, bool drv_en_active_level, bool wake_active_level, bool faultn_active_level) noexcept
Construct UART communication interface with pin active level configuration.
Definition tmc9660_comm_interface.hpp:1075
Definition bootloader_config.hpp:9
static constexpr uint8_t tmclChecksum(const uint8_t *bytes, size_t n, uint8_t start=0) noexcept
Calculate 8-bit checksum using sum of bytes method.
Definition tmc9660_comm_interface.hpp:170
TMC9660CtrlPin
TMC9660 control pin identifiers with board-agnostic naming.
Definition tmc9660_comm_interface.hpp:126
@ DRV_EN
Driver enable input (pin 21) - Enables motor driver outputs.
@ FAULTN
FAULT output signal (pin 20) - Open drain fault indication.
@ WAKE
Wake input (pin 19) - Wake from hibernation mode.
@ RST
External System Reset Input (pin 22) - Active high reset signal.
CommMode
Supported physical communication modes for TMC9660.
Definition tmc9660_comm_interface.hpp:113
@ SPI
SPI (Serial Peripheral Interface) mode - 4-wire synchronous communication.
GpioSignal
GPIO signal states with board-agnostic naming.
Definition tmc9660_comm_interface.hpp:140
@ ACTIVE
Active signal state (logical high)
@ INACTIVE
Inactive signal state (logical low)
@ OK
Command executed successfully.
SPIStatus
SPI status codes as per TMC9660 Parameter Mode specification.
Definition tmc9660_comm_interface.hpp:151
@ FIRST_CMD
First command after initialization.
@ NOT_READY
System busy, resend datagram.
@ OK
Operation successful.
@ CHECKSUM_ERROR
Checksum verification failed.
Frame structure for TMCL commands.
Definition tmc9660_comm_interface.hpp:369
static TMCLFrame fromSpi(std::span< const uint8_t, 8 > in) noexcept
Deserialize an SPI buffer into a TMCLFrame without status check.
Definition tmc9660_comm_interface.hpp:417
uint8_t motor
Motor or bank identifier (BYTE 2, bits 20-23).
Definition tmc9660_comm_interface.hpp:372
static bool fromSpiChecked(std::span< const uint8_t, 8 > in, TMCLFrame &out_frame) noexcept
Deserialize an SPI buffer with status and checksum validation.
Definition tmc9660_comm_interface.hpp:435
static bool fromUart(std::span< const uint8_t, 9 > in, uint8_t expected_addr, TMCLFrame &out_frame) noexcept
Deserialize a UART buffer with address and checksum validation.
Definition tmc9660_comm_interface.hpp:451
uint32_t value
32-bit data value (BYTE 3-6, bits 24-55).
Definition tmc9660_comm_interface.hpp:373
static constexpr uint8_t calculateChecksum(const uint8_t *bytes, size_t n) noexcept
Calculate 8-bit checksum (sum of bytes).
Definition tmc9660_comm_interface.hpp:467
uint16_t type
Parameter or command type (BYTE 1-2, bits 8-19).
Definition tmc9660_comm_interface.hpp:371
uint8_t opcode
Operation code field (BYTE 0, bits 0-7).
Definition tmc9660_comm_interface.hpp:370
void toUart(uint8_t addr, std::span< uint8_t, 9 > out) const noexcept
Serialize frame into 9-byte UART buffer, including sync bit and checksum.
Definition tmc9660_comm_interface.hpp:396
void toSpi(std::span< uint8_t, 8 > out) const noexcept
Serialize frame into 8-byte SPI buffer.
Definition tmc9660_comm_interface.hpp:379
Reply structure returned by TMCL command operations.
Definition tmc9660_comm_interface.hpp:189
bool isOK() const noexcept
Definition tmc9660_comm_interface.hpp:199
uint32_t extractRawValue(size_t offset) const noexcept
Extract 32-bit value from raw bytes at specified offset (MSB-first)
Definition tmc9660_comm_interface.hpp:211
static bool fromSpi(std::span< const uint8_t, 8 > in, TMCLReply &r, uint8_t sent_opcode=0, uint16_t sent_type=0) noexcept
Decode and validate reply from SPI TMCL datagram.
Definition tmc9660_comm_interface.hpp:259
SPIStatus spi_status
SPI status byte.
Definition tmc9660_comm_interface.hpp:190
uint32_t value
Optional returned value.
Definition tmc9660_comm_interface.hpp:193
std::array< uint8_t, 8 > rawBytes
Raw SPI bytes (8-byte) or UART bytes mapped to 8-byte format.
Definition tmc9660_comm_interface.hpp:196
std::string getVersionString() const noexcept
Extract version string from GetVersion reply (Command 136, Type 0)
Definition tmc9660_comm_interface.hpp:227
uint8_t opcode
Echoed operation code.
Definition tmc9660_comm_interface.hpp:192
static bool fromUart(std::span< const uint8_t, 9 > in, uint8_t addr, TMCLReply &r, uint8_t sent_opcode=0, uint16_t sent_type=0) noexcept
Decode and validate reply from UART TMCL datagram.
Definition tmc9660_comm_interface.hpp:320
uint8_t status
TMCL status code (100=OK, 101=LOADED)
Definition tmc9660_comm_interface.hpp:191
#define TMC9660_LOG_DEBUG(comm_obj, level, tag,...)
Compile-time debug logging control for TMC9660 library.
Definition tmc9660_comm_interface.hpp:101