149#ifndef TMC51X0_DISABLE_DEBUG_LOGGING
153#ifndef TMC51X0_LOG_LEVEL
154#define TMC51X0_LOG_LEVEL 4
158#if TMC51X0_LOG_LEVEL >= 0
159#define TMC51X0_LOGE(comm_obj, tag, ...) (comm_obj).LogDebug(::tmc51x0::LogLevel::Error, tag, __VA_ARGS__)
161#define TMC51X0_LOGE(comm_obj, tag, ...) ((void)0)
164#if TMC51X0_LOG_LEVEL >= 1
165#define TMC51X0_LOGW(comm_obj, tag, ...) (comm_obj).LogDebug(::tmc51x0::LogLevel::Warn, tag, __VA_ARGS__)
167#define TMC51X0_LOGW(comm_obj, tag, ...) ((void)0)
170#if TMC51X0_LOG_LEVEL >= 2
171#define TMC51X0_LOGI(comm_obj, tag, ...) (comm_obj).LogDebug(::tmc51x0::LogLevel::Info, tag, __VA_ARGS__)
173#define TMC51X0_LOGI(comm_obj, tag, ...) ((void)0)
176#if TMC51X0_LOG_LEVEL >= 3
177#define TMC51X0_LOGD(comm_obj, tag, ...) (comm_obj).LogDebug(::tmc51x0::LogLevel::Debug, tag, __VA_ARGS__)
179#define TMC51X0_LOGD(comm_obj, tag, ...) ((void)0)
182#if TMC51X0_LOG_LEVEL >= 4
183#define TMC51X0_LOGV(comm_obj, tag, ...) (comm_obj).LogDebug(::tmc51x0::LogLevel::Verbose, tag, __VA_ARGS__)
185#define TMC51X0_LOGV(comm_obj, tag, ...) ((void)0)
191#define TMC51X0_LOG_DEBUG(comm_obj, level, tag, ...) \
193 const int _lvl = static_cast<int>(level); \
195 TMC51X0_LOGE(comm_obj, tag, __VA_ARGS__); \
196 } else if (_lvl == 1) { \
197 TMC51X0_LOGW(comm_obj, tag, __VA_ARGS__); \
198 } else if (_lvl == 2) { \
199 TMC51X0_LOGI(comm_obj, tag, __VA_ARGS__); \
200 } else if (_lvl == 3) { \
201 TMC51X0_LOGD(comm_obj, tag, __VA_ARGS__); \
203 TMC51X0_LOGV(comm_obj, tag, __VA_ARGS__); \
210#define TMC51X0_LOGE(comm_obj, tag, ...) ((void)0)
211#define TMC51X0_LOGW(comm_obj, tag, ...) ((void)0)
212#define TMC51X0_LOGI(comm_obj, tag, ...) ((void)0)
213#define TMC51X0_LOGD(comm_obj, tag, ...) ((void)0)
214#define TMC51X0_LOGV(comm_obj, tag, ...) ((void)0)
215#define TMC51X0_LOG_DEBUG(comm_obj, level, tag, ...) ((void)0)
460 diag0 = active_level;
463 diag1 = active_level;
597 status.
value = status_byte;
625 return (
value & 0x02) != 0;
633 return (
value & 0x04) != 0;
647 return (
value & 0x10) != 0;
655 return (
value & 0x20) != 0;
662 [[nodiscard]]
bool StopLeft() const noexcept {
return (
value & 0x40) != 0; }
681 if (out ==
nullptr || cap == 0)
return;
684 "RST:%d STST:%d VEL:%d POS:%d STOP_L:%d STOP_R:%d SG2:%d DRV_ERR:%d",
696 [[nodiscard]]
const char *
ToString() const noexcept {
767 for (
size_t i = 0; i < 5; ++i) {
777 for (
size_t i = 0; i < 5; ++i) {
789 cmd.frame.fields.address_byte = addr & 0x7F;
802 (addr & 0x7F) | 0x80;
803 cmd.frame.fields.data_bytes[0] =
804 static_cast<uint8_t
>((val >> 24) & 0xFF);
805 cmd.frame.fields.data_bytes[1] =
static_cast<uint8_t
>((val >> 16) & 0xFF);
806 cmd.frame.fields.data_bytes[2] =
static_cast<uint8_t
>((val >> 8) & 0xFF);
807 cmd.frame.fields.data_bytes[3] =
static_cast<uint8_t
>(val & 0xFF);
841 size_t length)
noexcept {
845 for (
size_t i = 0; i < length; ++i) {
846 uint8_t current_byte = data[i];
849 for (uint8_t j = 0; j < 8; ++j) {
852 if (((crc >> 7) ^ (current_byte & 0x01)) != 0) {
853 crc = (crc << 1) ^ 0x07;
857 current_byte = current_byte >> 1;
931 [[nodiscard]]
size_t GetSize() const noexcept {
977 size_t crc_length = frame_size - 1;
992 size_t crc_length = frame_size - 1;
997 return calculated_crc ==
frame.
bytes[frame_size - 1];
1007 size_t frame_size =
GetSize();
1008 for (
size_t i = 0; i < frame_size; ++i) {
1012 if (frame_size < 8) {
1013 for (
size_t i = frame_size; i < 8; ++i) {
1024 size_t frame_size =
GetSize();
1025 for (
size_t i = 0; i < frame_size; ++i) {
1038 uint32_t value)
noexcept {
1043 uart_frame.frame.write_fields.sync_reserved = 0x05;
1046 uart_frame.frame.write_fields.node_addr = node_addr;
1049 uart_frame.frame.write_fields.rw_address = (reg_addr & 0x7F) | 0x80;
1052 uart_frame.frame.write_fields.data_bytes[0] =
1053 static_cast<uint8_t
>((value >> 24) & 0xFF);
1054 uart_frame.frame.write_fields.data_bytes[1] =
1055 static_cast<uint8_t
>((value >> 16) & 0xFF);
1056 uart_frame.frame.write_fields.data_bytes[2] =
1057 static_cast<uint8_t
>((value >> 8) & 0xFF);
1058 uart_frame.frame.write_fields.data_bytes[3] =
1059 static_cast<uint8_t
>(value & 0xFF);
1062 uart_frame.CalculateCrc();
1078 uart_frame.frame.read_request_fields.sync_reserved = 0x05;
1081 uart_frame.frame.read_request_fields.node_addr = node_addr;
1084 uart_frame.frame.read_request_fields.rw_address = reg_addr & 0x7F;
1087 uart_frame.CalculateCrc();
1159 return static_cast<const Derived *
>(
this)->
GetMode();
1175 uint8_t address_param = 0) noexcept {
1177 return static_cast<Derived *
>(
this)->
ReadRegister(address,
1195 uint8_t address_param = 0) noexcept {
1196 return static_cast<Derived *
>(
this)->
WriteRegister(address, value,
1207 return static_cast<Derived *
>(
this)->
GpioSet(pin, signal);
1216 return static_cast<Derived *
>(
this)->
GpioRead(pin);
1245 void DebugLog(
int level,
const char *tag,
const char *format,
1246 va_list args)
noexcept {
1247 static_cast<Derived *
>(
this)->
DebugLog(level, tag, format, args);
1256 static_cast<Derived *
>(
this)->
DelayMs(ms);
1264 static_cast<Derived *
>(
this)->
DelayUs(us);
1300 uint32_t power_on_settle_ms = 20) noexcept {
1406#ifndef TMC51X0_DISABLE_DEBUG_LOGGING
1407 void LogDebug(
int level,
const char *tag,
const char *format, ...) noexcept {
1409 va_start(args, format);
1412 DebugLog(level, tag, format, args);
1421 va_start(args, format);
1422 DebugLog(
static_cast<int>(level), tag, format, args);
1427 inline void LogDebug(
int ,
const char * ,
1428 const char * , ...) noexcept {
1434 const char * , ...) noexcept {
1659template <
typename Derived>
1671#ifndef TMC51X0_SPI_MAX_CHAIN_DEVICES
1672#define TMC51X0_SPI_MAX_CHAIN_DEVICES 8
1676 "TMC51X0_SPI_MAX_CHAIN_DEVICES must be >= 1");
1678 "TMC51X0_SPI_MAX_CHAIN_DEVICES must be <= 255");
1758 if (max_devices == 0) {
1783 uint8_t cmd_bytes[5];
1788 size_t transfer_bytes =
static_cast<size_t>(max_devices + 2) * 5;
1805 *
static_cast<Derived *
>(
this), 2,
"SPI",
1806 "AutoDetectChainLength: Probing up to %u devices, transfer_bytes=%zu, "
1807 "cmd_pattern=%02X %02X %02X %02X %02X",
1808 max_devices, transfer_bytes, cmd_bytes[0], cmd_bytes[1], cmd_bytes[2],
1809 cmd_bytes[3], cmd_bytes[4]);
1812 if (!
SpiTransfer(tx_buf, rx_buf, transfer_bytes)) {
1814 "AutoDetectChainLength: SPI transfer failed");
1823 uint8_t detected_length = 0;
1828 for (uint8_t n = max_devices; n >= 1; --n) {
1829 size_t loopback_offset =
static_cast<size_t>(n) * 5;
1831 if (loopback_offset + 4 < transfer_bytes) {
1835 bool exact_match =
true;
1837 for (uint8_t i = 0; i < 5; ++i) {
1838 if (rx_buf[loopback_offset + i] != cmd_bytes[i]) {
1839 exact_match =
false;
1847 detected_length = n;
1849 "AutoDetectChainLength: Found EXACT command "
1850 "pattern match at offset "
1851 "%zu (expected for n=%u), chain length = %u",
1852 loopback_offset, n, detected_length);
1859 if (detected_length == 0) {
1861 "AutoDetectChainLength: Exact command pattern not "
1862 "found in received data");
1864 *
static_cast<Derived *
>(
this), 3,
"SPI",
1865 "AutoDetectChainLength: Expected pattern: %02X %02X %02X %02X %02X",
1866 cmd_bytes[0], cmd_bytes[1], cmd_bytes[2], cmd_bytes[3], cmd_bytes[4]);
1869 for (uint8_t n = 1; n <= 3 && n <= max_devices; ++n) {
1870 size_t loopback_offset =
static_cast<size_t>(n) * 5;
1871 if (loopback_offset + 4 < transfer_bytes) {
1873 *
static_cast<Derived *
>(
this), 3,
"SPI",
1874 "AutoDetectChainLength: At offset %zu (n=%u): %02X %02X %02X "
1876 loopback_offset, n, rx_buf[loopback_offset],
1877 rx_buf[loopback_offset + 1], rx_buf[loopback_offset + 2],
1878 rx_buf[loopback_offset + 3], rx_buf[loopback_offset + 4]);
1885 if (detected_length > 0) {
1887 "AutoDetectChainLength: Detected chain length = %u",
1896 "AutoDetectChainLength: Command pattern not found, "
1897 "assuming single chip");
1906 return detected_length;
1923 size_t length)
noexcept {
1924 return static_cast<Derived *
>(
this)->
SpiTransfer(tx, rx, length);
1992 uint8_t daisy_chain_position = 0) noexcept {
1996 if (daisy_chain_position > 0) {
1998 "ReadRegister(0x%02X, daisy_chain=%u)", address,
1999 daisy_chain_position);
2002 "ReadRegister(0x%02X)", address);
2020 *
static_cast<Derived *
>(
this), 1,
"SPI",
2021 "ReadRegister: Chain length unknown for daisy_chain_position=%u. "
2022 "Cannot proceed without chain length.",
2023 daisy_chain_position);
2035 uint8_t k = daisy_chain_position;
2042 *
static_cast<Derived *
>(
this), 1,
"SPI",
2043 "ReadRegister: Chain length %u exceeds TMC51X0_SPI_MAX_CHAIN_DEVICES=%u",
2052 *
static_cast<Derived *
>(
this), 1,
"SPI",
2053 "ReadRegister: Invalid daisy_chain_position=%u for chain length=%u. "
2054 "Position must be < chain length.",
2060 size_t sending_bytes =
2061 static_cast<size_t>(k + 1) * 5;
2062 size_t receiving_bytes =
static_cast<size_t>(n - k) *
2064 size_t transfer_bytes = std::max(sending_bytes, receiving_bytes);
2067 size_t response_byte_offset =
static_cast<size_t>(n - k - 1) * 5;
2070 if (transfer_bytes < receiving_bytes) {
2072 "ReadRegister: Transfer size %zu bytes < receiving "
2073 "requirement %zu bytes. "
2074 "Response extraction may fail.",
2075 transfer_bytes, receiving_bytes);
2080 if (response_byte_offset + 4 >= transfer_bytes) {
2082 *
static_cast<Derived *
>(
this), 1,
"SPI",
2083 "ReadRegister: Response offset %zu + 4 >= transfer size %zu. "
2084 "Cannot read full 5-byte response.",
2085 response_byte_offset, transfer_bytes);
2091 "ReadRegister: transfer_bytes=%zu exceeds scratch cap=%zu",
2114 if (!
SpiTransfer(tx_buf, rx_buf, transfer_bytes)) {
2122 "Read 0x%02X=0x00000000: [TX1] %02X %02X %02X %02X %02X "
2123 "/ RX1 %02X %02X %02X %02X %02X",
2124 address, tx_buf[0], tx_buf[1], tx_buf[2], tx_buf[3],
2125 tx_buf[4], rx_buf[response_byte_offset],
2126 (response_byte_offset + 1 < transfer_bytes)
2127 ? rx_buf[response_byte_offset + 1]
2129 (response_byte_offset + 2 < transfer_bytes)
2130 ? rx_buf[response_byte_offset + 2]
2132 (response_byte_offset + 3 < transfer_bytes)
2133 ? rx_buf[response_byte_offset + 3]
2135 (response_byte_offset + 4 < transfer_bytes)
2136 ? rx_buf[response_byte_offset + 4]
2150 if (!
SpiTransfer(tx_buf, rx_buf, transfer_bytes)) {
2157 char status_bits[128];
2167 "Read 0x%02X: TX2 %02X %02X %02X %02X %02X / "
2168 "[RX2] %02X %02X %02X %02X %02X (STATUS=0x%02X)",
2169 address, tx_buf[0], tx_buf[1], tx_buf[2], tx_buf[3],
2170 tx_buf[4], rx_buf[response_byte_offset],
2171 (response_byte_offset + 1 < transfer_bytes)
2172 ? rx_buf[response_byte_offset + 1]
2174 (response_byte_offset + 2 < transfer_bytes)
2175 ? rx_buf[response_byte_offset + 2]
2177 (response_byte_offset + 3 < transfer_bytes)
2178 ? rx_buf[response_byte_offset + 3]
2180 (response_byte_offset + 4 < transfer_bytes)
2181 ? rx_buf[response_byte_offset + 4]
2183 rx_buf[response_byte_offset]);
2189 *
static_cast<Derived *
>(
this), 3,
"SPI",
2205 if (response_byte_offset >= transfer_bytes) {
2207 *
static_cast<Derived *
>(
this), 1,
"SPI",
2208 "Read register 0x%02X: Response offset %zu exceeds buffer size %zu",
2209 address, response_byte_offset, transfer_bytes);
2225 char info_flags[64] =
"";
2229 snprintf(info_flags,
sizeof(info_flags),
" [%s%s%s%s%s%s%s]",
2235 status.
StopLeft() ?
"STOP_L " :
"",
2240 "Read 0x%02X: STATUS=0x%02X DRV_ERR%s", address,
2241 status.
value, info_flags);
2245 size_t log_rx_bytes = (transfer_bytes < 8) ? transfer_bytes : 8;
2247 "Read register 0x%02X: RX[0..%zu] %02X %02X %02X %02X "
2248 "%02X %02X %02X %02X "
2249 "| SPI_STATUS=0x%02X [%s%s%s%s%s%s%s%s]",
2250 address, log_rx_bytes - 1, rx_buf[0],
2251 (log_rx_bytes > 1) ? rx_buf[1] : 0,
2252 (log_rx_bytes > 2) ? rx_buf[2] : 0,
2253 (log_rx_bytes > 3) ? rx_buf[3] : 0,
2254 (log_rx_bytes > 4) ? rx_buf[4] : 0,
2255 (log_rx_bytes > 5) ? rx_buf[5] : 0,
2256 (log_rx_bytes > 6) ? rx_buf[6] : 0,
2257 (log_rx_bytes > 7) ? rx_buf[7] : 0, status.
value,
2264 status.
StopLeft() ?
"STOP_L " :
"",
2272 if (response_byte_offset + 4 >= transfer_bytes) {
2274 *
static_cast<Derived *
>(
this), 1,
"SPI",
2275 "Read register 0x%02X: Data offset %zu+4 exceeds buffer size %zu",
2276 address, response_byte_offset, transfer_bytes);
2282 value = (
static_cast<uint32_t
>(rx_buf[response_byte_offset + 1]) << 24) |
2283 (
static_cast<uint32_t
>(rx_buf[response_byte_offset + 2]) << 16) |
2284 (
static_cast<uint32_t
>(rx_buf[response_byte_offset + 3]) << 8) |
2285 static_cast<uint32_t
>(rx_buf[response_byte_offset + 4]);
2333 uint8_t daisy_chain_position = 0) noexcept {
2336 if (daisy_chain_position > 0) {
2338 "WriteRegister(0x%02X=0x%08X, daisy_chain=%u)", address,
2339 value, daisy_chain_position);
2342 "WriteRegister(0x%02X=0x%08X)", address, value);
2360 *
static_cast<Derived *
>(
this), 1,
"SPI",
2361 "WriteRegister: Chain length unknown for daisy_chain_position=%u. "
2362 "Cannot proceed without chain length.",
2363 daisy_chain_position);
2377 uint8_t k = daisy_chain_position;
2383 *
static_cast<Derived *
>(
this), 1,
"SPI",
2384 "WriteRegister: Chain length %u exceeds TMC51X0_SPI_MAX_CHAIN_DEVICES=%u",
2393 *
static_cast<Derived *
>(
this), 1,
"SPI",
2394 "WriteRegister: Invalid daisy_chain_position=%u for chain length=%u. "
2395 "Position must be < chain length.",
2401 size_t sending_bytes =
2402 static_cast<size_t>(k + 1) * 5;
2403 size_t receiving_bytes =
static_cast<size_t>(n - k) *
2405 size_t transfer_bytes = std::max(sending_bytes, receiving_bytes);
2408 size_t response_byte_offset =
static_cast<size_t>(n - k - 1) * 5;
2411 if (transfer_bytes < receiving_bytes) {
2413 "WriteRegister: Transfer size %zu bytes < receiving "
2414 "requirement %zu bytes. "
2415 "Response extraction may fail.",
2416 transfer_bytes, receiving_bytes);
2421 if (response_byte_offset + 4 >= transfer_bytes) {
2423 *
static_cast<Derived *
>(
this), 1,
"SPI",
2424 "WriteRegister: Response offset %zu + 4 >= transfer size %zu. "
2425 "Cannot read full 5-byte response.",
2426 response_byte_offset, transfer_bytes);
2432 "WriteRegister: transfer_bytes=%zu exceeds scratch cap=%zu",
2455 if (!
SpiTransfer(tx_buf, rx_buf, transfer_bytes)) {
2461 "Write 0x%02X=0x%08X: [TX1] %02X %02X %02X %02X %02X / "
2462 "RX1 %02X %02X %02X %02X %02X",
2463 address, value, tx_buf[0], tx_buf[1], tx_buf[2],
2464 tx_buf[3], tx_buf[4], rx_buf[response_byte_offset],
2465 (response_byte_offset + 1 < transfer_bytes)
2466 ? rx_buf[response_byte_offset + 1]
2468 (response_byte_offset + 2 < transfer_bytes)
2469 ? rx_buf[response_byte_offset + 2]
2471 (response_byte_offset + 3 < transfer_bytes)
2472 ? rx_buf[response_byte_offset + 3]
2474 (response_byte_offset + 4 < transfer_bytes)
2475 ? rx_buf[response_byte_offset + 4]
2486 if (response_byte_offset >= transfer_bytes) {
2488 *
static_cast<Derived *
>(
this), 1,
"SPI",
2489 "Write register 0x%02X: Response offset %zu exceeds buffer size %zu",
2490 address, response_byte_offset, transfer_bytes);
2508 char info_flags[64] =
"";
2513 snprintf(info_flags,
sizeof(info_flags),
" [%s%s%s%s%s%s%s]",
2519 status1.
StopLeft() ?
"STOP_L " :
"",
2524 "Write 0x%02X (TX1): STATUS=0x%02X DRV_ERR%s",
2525 address, status1.
value, info_flags);
2529 size_t log_rx1_bytes = (transfer_bytes < 8) ? transfer_bytes : 8;
2531 "Write register 0x%02X (TX1): RX[0..%zu] %02X %02X "
2532 "%02X %02X %02X %02X %02X %02X | "
2533 "SPI_STATUS=0x%02X [%s%s%s%s%s%s%s%s]",
2534 address, log_rx1_bytes - 1, rx_buf[0],
2535 (log_rx1_bytes > 1) ? rx_buf[1] : 0,
2536 (log_rx1_bytes > 2) ? rx_buf[2] : 0,
2537 (log_rx1_bytes > 3) ? rx_buf[3] : 0,
2538 (log_rx1_bytes > 4) ? rx_buf[4] : 0,
2539 (log_rx1_bytes > 5) ? rx_buf[5] : 0,
2540 (log_rx1_bytes > 6) ? rx_buf[6] : 0,
2541 (log_rx1_bytes > 7) ? rx_buf[7] : 0, status1.
value,
2548 status1.
StopLeft() ?
"STOP_L " :
"",
2566 if (!
SpiTransfer(tx_buf, rx_buf, transfer_bytes)) {
2573 char status2_bits[128];
2585 "Write 0x%02X: TX2 %02X %02X %02X %02X %02X "
2586 "/ [RX2] %02X %02X %02X %02X %02X (STATUS=0x%02X)",
2587 address, tx_buf[0], tx_buf[1], tx_buf[2], tx_buf[3],
2588 tx_buf[4], rx_buf[response_byte_offset],
2589 (response_byte_offset + 1 < transfer_bytes)
2590 ? rx_buf[response_byte_offset + 1]
2592 (response_byte_offset + 2 < transfer_bytes)
2593 ? rx_buf[response_byte_offset + 2]
2595 (response_byte_offset + 3 < transfer_bytes)
2596 ? rx_buf[response_byte_offset + 3]
2598 (response_byte_offset + 4 < transfer_bytes)
2599 ? rx_buf[response_byte_offset + 4]
2601 rx_buf[response_byte_offset]);
2605 *
static_cast<Derived *
>(
this), 3,
"SPI",
2611 if (response_byte_offset >= transfer_bytes) {
2613 "Write register 0x%02X (TX2): Response offset %zu "
2614 "exceeds buffer size %zu",
2615 address, response_byte_offset, transfer_bytes);
2628 char info_flags[64] =
"";
2633 snprintf(info_flags,
sizeof(info_flags),
" [%s%s%s%s%s%s%s]",
2639 status2.
StopLeft() ?
"STOP_L " :
"",
2644 "Write 0x%02X (TX2): STATUS=0x%02X DRV_ERR%s",
2645 address, status2.
value, info_flags);
2649 size_t log_rx2_bytes = (transfer_bytes < 8) ? transfer_bytes : 8;
2651 "Write register 0x%02X (TX2): RX[0..%zu] %02X %02X "
2652 "%02X %02X %02X %02X %02X %02X | "
2653 "SPI_STATUS=0x%02X [%s%s%s%s%s%s%s%s]",
2654 address, log_rx2_bytes - 1, rx_buf[0],
2655 (log_rx2_bytes > 1) ? rx_buf[1] : 0,
2656 (log_rx2_bytes > 2) ? rx_buf[2] : 0,
2657 (log_rx2_bytes > 3) ? rx_buf[3] : 0,
2658 (log_rx2_bytes > 4) ? rx_buf[4] : 0,
2659 (log_rx2_bytes > 5) ? rx_buf[5] : 0,
2660 (log_rx2_bytes > 6) ? rx_buf[6] : 0,
2661 (log_rx2_bytes > 7) ? rx_buf[7] : 0, status2.
value,
2668 status2.
StopLeft() ?
"STOP_L " :
"",
2676 if (response_byte_offset + 4 < transfer_bytes) {
2677 uint32_t returned_value =
2678 (
static_cast<uint32_t
>(rx_buf[response_byte_offset + 1]) << 24) |
2679 (
static_cast<uint32_t
>(rx_buf[response_byte_offset + 2]) << 16) |
2680 (
static_cast<uint32_t
>(rx_buf[response_byte_offset + 3]) << 8) |
2681 static_cast<uint32_t
>(rx_buf[response_byte_offset + 4]);
2683 if (returned_value != value) {
2685 "WriteRegister(0x%02X): Write verification failed - "
2686 "wrote 0x%08X, got back 0x%08X",
2687 address, value, returned_value);
2691 "WriteRegister(0x%02X): Write verification passed - "
2692 "wrote 0x%08X, got back 0x%08X",
2693 address, value, returned_value);
2761 const char *context)
noexcept {
2764 uint8_t detected_length =
2766 if (detected_length == 0) {
2769 *
static_cast<Derived *
>(
this), 1,
"SPI",
2770 "%s: Auto-detection failed, but daisy_chain_position=%u > 0. "
2771 "Chain length is required for correct response extraction. "
2772 "Operation failed.",
2773 context, daisy_chain_position);
2791 if (detected_length > 0 &&
2794 "%s: DAISY CHAIN LENGTH MISMATCH! "
2795 "User specified: %u, Auto-detected: %u. "
2796 "Response extraction will be incorrect. "
2797 "Call SetDaisyChainLength(%u) to fix.",
2799 detected_length, detected_length);
2804 }
else if (detected_length > 0) {
2808 "%s: Chain length verified: %u devices", context,
2824 "%s: DAISY CHAIN LENGTH MISMATCH! "
2825 "Specified: %u, Auto-detected: %u. "
2826 "Updating to detected length.",
2934template <
typename Derived>
2977 return static_cast<Derived *
>(
this)->
SetNaiPin(active);
2999 bool active =
false;
3000 return static_cast<Derived *
>(
this)->
GetNaoPin(active);
3010 return static_cast<Derived *
>(
this)->
UartSend(data, length);
3020 return static_cast<Derived *
>(
this)->
UartReceive(data, length);
3031 uint8_t node_address = 0) noexcept {
3034 uint8_t node_addr = node_address & 0x7F;
3038 std::array<uint8_t, 8> tx_buf{};
3039 read_request.
GetFrame(tx_buf.data());
3040 size_t tx_size = read_request.
GetSize();
3043 *
static_cast<Derived *
>(
this), 3,
"UART",
3044 "Read register 0x%02X (NodeAddr=0x%02X): TX %02X %02X %02X %02X",
3045 address, node_addr, tx_buf[0], tx_buf[1], tx_buf[2], tx_buf[3]);
3047 if (!
UartSend(tx_buf.data(), tx_size)) {
3052 std::array<uint8_t, 8> rx_buf{};
3061 *
static_cast<Derived *
>(
this), 3,
"UART",
3062 "Read register 0x%02X: RX %02X %02X %02X %02X %02X %02X %02X %02X",
3063 address, rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3], rx_buf[4],
3064 rx_buf[5], rx_buf[6], rx_buf[7]);
3069 "Read register 0x%02X: CRC8 verification failed",
3076 "Read register 0x%02X: Invalid frame structure",
3095 uint8_t node_address = 0) noexcept {
3097 uint8_t node_addr = node_address & 0x7F;
3101 std::array<uint8_t, 8> tx_buf{};
3102 write_frame.
GetFrame(tx_buf.data());
3103 size_t tx_size = write_frame.
GetSize();
3106 "Write register 0x%02X = 0x%08X (NodeAddr=0x%02X): TX "
3107 "%02X %02X %02X %02X "
3108 "%02X %02X %02X %02X",
3109 address, node_addr, tx_buf[0], tx_buf[1], tx_buf[2],
3110 tx_buf[3], tx_buf[4], tx_buf[5], tx_buf[6], tx_buf[7]);
3112 if (!
UartSend(tx_buf.data(), tx_size)) {
CRTP-based communication interface for register read/write operations.
Definition tmc51x0_comm_interface.hpp:1144
Result< GpioSignal > GpioRead(TMC51x0CtrlPin pin) noexcept
Read GPIO pin signal state (input state)
Definition tmc51x0_comm_interface.hpp:1215
void DebugLog(int level, const char *tag, const char *format, va_list args) noexcept
Debug logging function for detailed debugging information.
Definition tmc51x0_comm_interface.hpp:1245
~CommInterface()=default
Protected destructor.
CommInterface(const CommInterface &)=delete
Result< uint32_t > ReadRegister(uint8_t address, uint8_t address_param=0) noexcept
Read a 32-bit register from the TMC5160.
Definition tmc51x0_comm_interface.hpp:1174
void LogDebug(LogLevel level, const char *tag, const char *format,...) noexcept
LogDebug overload that accepts the driver-native LogLevel enum.
Definition tmc51x0_comm_interface.hpp:1419
Result< void > SetPowerEnabled(bool enabled) noexcept
Enable/disable power to the TMC51x0 (optional)
Definition tmc51x0_comm_interface.hpp:1282
void LogDebug(int level, const char *tag, const char *format,...) noexcept
Public debug logging wrapper for external classes.
Definition tmc51x0_comm_interface.hpp:1407
CommInterface & operator=(const CommInterface &)=delete
CommInterface & operator=(CommInterface &&)=default
CommInterface(CommInterface &&)=default
CommInterface() noexcept=default
Construct communication interface.
Result< void > SetClkFreq(uint32_t frequency_hz) noexcept
Set external clock frequency on CLK pin (optional)
Definition tmc51x0_comm_interface.hpp:1376
Result< void > WriteRegister(uint8_t address, uint32_t value, uint8_t address_param=0) noexcept
Write a 32-bit register to the TMC5160.
Definition tmc51x0_comm_interface.hpp:1194
CommMode GetMode() const noexcept
Get the underlying communication mode used by this interface.
Definition tmc51x0_comm_interface.hpp:1158
Result< void > GpioSet(TMC51x0CtrlPin pin, GpioSignal signal) noexcept
Set GPIO pin signal state (output control)
Definition tmc51x0_comm_interface.hpp:1206
void DelayMs(uint32_t ms) noexcept
Delay execution for specified milliseconds.
Definition tmc51x0_comm_interface.hpp:1255
void DelayUs(uint32_t us) noexcept
Delay execution for specified microseconds.
Definition tmc51x0_comm_interface.hpp:1263
Result< void > PowerCycle(uint32_t power_off_ms=20, uint32_t power_on_settle_ms=20) noexcept
Power-cycle the TMC51x0 (optional)
Definition tmc51x0_comm_interface.hpp:1299
Result< void > GpioSetInactive(TMC51x0CtrlPin pin) noexcept
Set GPIO pin to inactive state (convenience method)
Definition tmc51x0_comm_interface.hpp:1233
Result< void > GpioSetActive(TMC51x0CtrlPin pin) noexcept
Set GPIO pin to active state (convenience method)
Definition tmc51x0_comm_interface.hpp:1224
Result type for operations that return a value.
Definition tmc51x0_result.hpp:90
CRTP-based SPI implementation of TMC5160CommInterface.
Definition tmc51x0_comm_interface.hpp:1660
CommMode GetMode() const noexcept
Get communication mode (always SPI for this interface)
Definition tmc51x0_comm_interface.hpp:1913
Result< void > EnsureChainLengthKnown(uint8_t daisy_chain_position, const char *context) noexcept
Ensure chain length is known and verified for daisy-chain operations.
Definition tmc51x0_comm_interface.hpp:2760
void SetDaisyChainLength(uint8_t total_length) noexcept
Set the total number of devices in the daisy chain.
Definition tmc51x0_comm_interface.hpp:1708
Result< void > SpiTransfer(const uint8_t *tx, uint8_t *rx, size_t length) noexcept
Low-level SPI transfer for register read/write.
Definition tmc51x0_comm_interface.hpp:1922
uint8_t total_chain_length_
Total number of devices in the daisy chain.
Definition tmc51x0_comm_interface.hpp:2710
SpiCommInterface(SpiCommInterface &&)=default
Result< void > WriteRegister(uint8_t address, uint32_t value, uint8_t daisy_chain_position=0) noexcept
Write a 32-bit register via SPI.
Definition tmc51x0_comm_interface.hpp:2332
std::array< uint8_t, kSpiScratchBytes > rx_scratch_
Definition tmc51x0_comm_interface.hpp:2735
SpiCommInterface(const SpiCommInterface &)=delete
~SpiCommInterface()=default
Protected destructor.
uint8_t AutoDetectChainLength(uint8_t max_devices=8) noexcept
Auto-detect the daisy chain length by sending a unique command that loops back.
Definition tmc51x0_comm_interface.hpp:1757
bool chain_length_verified_
Definition tmc51x0_comm_interface.hpp:2714
SpiCommInterface & operator=(SpiCommInterface &&)=default
SpiCommInterface & operator=(const SpiCommInterface &)=delete
std::array< uint8_t, kSpiScratchBytes > tx_scratch_
Definition tmc51x0_comm_interface.hpp:2734
uint8_t user_specified_chain_length_
Definition tmc51x0_comm_interface.hpp:2712
SpiCommInterface() noexcept
Construct SPI communication interface.
Definition tmc51x0_comm_interface.hpp:1689
static constexpr size_t kSpiScratchBytes
Definition tmc51x0_comm_interface.hpp:1680
uint32_t drv_err_log_count_
Definition tmc51x0_comm_interface.hpp:2739
Result< uint32_t > ReadRegister(uint8_t address, uint8_t daisy_chain_position=0) noexcept
Read a 32-bit register via SPI.
Definition tmc51x0_comm_interface.hpp:1991
uint8_t GetDaisyChainLength() const noexcept
Get the total number of devices in the daisy chain.
Definition tmc51x0_comm_interface.hpp:1719
CRTP-based UART implementation of TMC5160CommInterface.
Definition tmc51x0_comm_interface.hpp:2935
~UartCommInterface()=default
Protected destructor.
Result< bool > GetNaoPin() noexcept
Read NAO (Next Address Output) pin state.
Definition tmc51x0_comm_interface.hpp:2998
UartCommInterface(const UartCommInterface &)=delete
Result< void > UartSend(const uint8_t *data, size_t length) noexcept
Send raw bytes via UART.
Definition tmc51x0_comm_interface.hpp:3009
Result< uint32_t > ReadRegister(uint8_t address, uint8_t node_address=0) noexcept
Read a 32-bit register via UART.
Definition tmc51x0_comm_interface.hpp:3030
UartCommInterface & operator=(const UartCommInterface &)=delete
CommMode GetMode() const noexcept
Get communication mode (always UART for this interface)
Definition tmc51x0_comm_interface.hpp:2957
Result< void > WriteRegister(uint8_t address, uint32_t value, uint8_t node_address=0) noexcept
Write a 32-bit register via UART.
Definition tmc51x0_comm_interface.hpp:3094
UartCommInterface() noexcept
Construct UART communication interface with pin active level configuration.
Definition tmc51x0_comm_interface.hpp:2951
Result< void > UartReceive(uint8_t *data, size_t length) noexcept
Receive raw bytes via UART.
Definition tmc51x0_comm_interface.hpp:3019
UartCommInterface & operator=(UartCommInterface &&)=default
Result< void > SetNaiPin(bool active) noexcept
Set NAI (Next Address Input) pin state for daisy chaining.
Definition tmc51x0_comm_interface.hpp:2976
UartCommInterface(UartCommInterface &&)=default
Definition tmc51x0_register_defs.cpp:10
GpioSignal
GPIO signal states with board-agnostic naming.
Definition tmc51x0_comm_interface.hpp:319
@ ACTIVE
Active signal state (logical high)
@ INACTIVE
Inactive signal state (logical low)
UartFrameType
TMC5160 UART frame types.
Definition tmc51x0_comm_interface.hpp:867
@ ReadReply
Read access reply datagram (8 bytes: 7 bytes + CRC)
@ WriteAccess
Write access datagram (8 bytes: 7 bytes + CRC)
@ ReadRequest
Read access request datagram (4 bytes: 3 bytes + CRC)
LogLevel
Driver-native log levels for debug output.
Definition tmc51x0_comm_interface.hpp:134
static constexpr uint8_t calculateCrc8(const uint8_t *data, size_t length) noexcept
Calculate CRC8 checksum for UART communication.
Definition tmc51x0_comm_interface.hpp:840
@ UNSUPPORTED
Feature not supported by this chip variant.
@ COMM_ERROR
Communication interface error (SPI/UART)
TMC51x0CtrlPin
TMC51x0 control pin identifiers with board-agnostic naming.
Definition tmc51x0_comm_interface.hpp:258
@ EN
Enable pin (DRV_ENN, pin 28) - Active HIGH disables power stage.
CommMode
Supported physical communication modes for TMC51x0.
Definition tmc51x0_comm_interface.hpp:221
Pin active level configuration structure.
Definition tmc51x0_comm_interface.hpp:352
bool diag0
Definition tmc51x0_comm_interface.hpp:365
bool diag1
Definition tmc51x0_comm_interface.hpp:367
bool dir
DIR pin (REFR_DIR, pin 18): HIGH=active (active HIGH)
Definition tmc51x0_comm_interface.hpp:355
bool clk
CLK (pin 12): HIGH=active (active HIGH, if used as output)
Definition tmc51x0_comm_interface.hpp:385
bool encn
Definition tmc51x0_comm_interface.hpp:375
bool dco
Definition tmc51x0_comm_interface.hpp:381
bool GetActiveLevel(TMC51x0CtrlPin pin) const noexcept
Get active level for a specific pin.
Definition tmc51x0_comm_interface.hpp:398
bool ref_right
REFR_DIR (pin 18): LOW=active (typically active LOW)
Definition tmc51x0_comm_interface.hpp:361
bool step
STEP pin (REFL_STEP, pin 17): HIGH=active (active HIGH)
Definition tmc51x0_comm_interface.hpp:356
bool ref_left
REFL_STEP (pin 17): LOW=active (typically active LOW)
Definition tmc51x0_comm_interface.hpp:359
bool enca
Definition tmc51x0_comm_interface.hpp:371
bool encb
Definition tmc51x0_comm_interface.hpp:373
bool spi_mode
SPI_MODE (pin 22): HIGH=SPI mode (active HIGH)
Definition tmc51x0_comm_interface.hpp:389
void SetActiveLevel(TMC51x0CtrlPin pin, bool active_level) noexcept
Set active level for a specific pin.
Definition tmc51x0_comm_interface.hpp:442
bool dcin
DCIN (pin 24): HIGH=active (active HIGH)
Definition tmc51x0_comm_interface.hpp:380
bool en
EN pin (DRV_ENN, pin 28): LOW=enable (active LOW)
Definition tmc51x0_comm_interface.hpp:354
bool sd_mode
SD_MODE (pin 21): HIGH=External Step/Dir (active HIGH)
Definition tmc51x0_comm_interface.hpp:390
bool dcen
DCEN (pin 23): HIGH=active (active HIGH)
Definition tmc51x0_comm_interface.hpp:379
TMC5160 SPI command structure with union-based frame representation.
Definition tmc51x0_comm_interface.hpp:720
void SetFrame(const uint8_t *bytes) noexcept
Set the 5-byte frame from raw bytes.
Definition tmc51x0_comm_interface.hpp:766
static SpiCommand Write(uint8_t addr, uint32_t val) noexcept
Construct a write command.
Definition tmc51x0_comm_interface.hpp:799
static SpiCommand Read(uint8_t addr) noexcept
Construct a read command.
Definition tmc51x0_comm_interface.hpp:786
uint8_t GetAddress() const noexcept
Get register address (bits 6-0 of address byte)
Definition tmc51x0_comm_interface.hpp:739
void GetFrame(uint8_t *bytes) const noexcept
Get the 5-byte frame as raw bytes.
Definition tmc51x0_comm_interface.hpp:776
union tmc51x0::SpiCommand::Frame frame
The 40-bit SPI frame.
bool IsWrite() const noexcept
Check if this is a write command.
Definition tmc51x0_comm_interface.hpp:747
uint32_t GetValue() const noexcept
Get 32-bit data value (for writes) or dummy data (for reads)
Definition tmc51x0_comm_interface.hpp:755
TMC5160 SPI response structure.
Definition tmc51x0_comm_interface.hpp:819
uint32_t value
Definition tmc51x0_comm_interface.hpp:821
SpiStatus status
SPI_STATUS flags from the chip.
Definition tmc51x0_comm_interface.hpp:820
bool success
true if the transport transaction succeeded
Definition tmc51x0_comm_interface.hpp:823
SPI_STATUS structure - status flags returned with each SPI datagram.
Definition tmc51x0_comm_interface.hpp:587
bool PositionReached() const noexcept
Get position reached flag (bit 5)
Definition tmc51x0_comm_interface.hpp:654
static SpiStatus FromByte(uint8_t status_byte) noexcept
Extract SPI_STATUS from response byte.
Definition tmc51x0_comm_interface.hpp:595
bool StopRight() const noexcept
Get stop right switch flag (bit 7)
Definition tmc51x0_comm_interface.hpp:668
const char * ToString() const noexcept
Format status flags as human-readable string.
Definition tmc51x0_comm_interface.hpp:696
bool StopLeft() const noexcept
Get stop left switch flag (bit 6)
Definition tmc51x0_comm_interface.hpp:662
void FormatStatusBits(char *out, size_t cap) const noexcept
Format status bits as compact string (bit names and values)
Definition tmc51x0_comm_interface.hpp:680
bool StallGuard2() const noexcept
Get StallGuard2 flag (bit 2)
Definition tmc51x0_comm_interface.hpp:632
bool DriverError() const noexcept
Get driver error flag (bit 1)
Definition tmc51x0_comm_interface.hpp:624
uint8_t value
Raw SPI_STATUS byte value.
Definition tmc51x0_comm_interface.hpp:588
bool HasError() const noexcept
Check if any error flags are set.
Definition tmc51x0_comm_interface.hpp:609
bool ResetFlag() const noexcept
Get reset flag (bit 0)
Definition tmc51x0_comm_interface.hpp:618
bool Standstill() const noexcept
Get standstill flag (bit 3)
Definition tmc51x0_comm_interface.hpp:640
bool VelocityReached() const noexcept
Get velocity reached flag (bit 4)
Definition tmc51x0_comm_interface.hpp:646
TMC51x0 GPIO pin configuration structure.
Definition tmc51x0_comm_interface.hpp:521
int dc_out_pin
DC Step ready output (ENCN_DCO_CFG6, pin 25) - Same as enc_n_pin.
Definition tmc51x0_comm_interface.hpp:549
int clk_pin
Clock input (CLK, pin 12) - Optional.
Definition tmc51x0_comm_interface.hpp:553
int enc_b_pin
Encoder B (ENCB_DCEN_CFG4, pin 23) - Same as dc_en_pin.
Definition tmc51x0_comm_interface.hpp:541
int ref_left_pin
Left reference switch (REFL_STEP, pin 17) - Same as step_pin.
Definition tmc51x0_comm_interface.hpp:530
int diag1_pin
DIAG1 pin (DIAG1_SWP, pin 27) - Optional.
Definition tmc51x0_comm_interface.hpp:537
int dc_en_pin
Definition tmc51x0_comm_interface.hpp:547
int en_pin
EN pin (DRV_ENN, pin 28) - Required.
Definition tmc51x0_comm_interface.hpp:523
int enc_n_pin
Encoder N (ENCN_DCO_CFG6, pin 25) - Same as dc_out_pin.
Definition tmc51x0_comm_interface.hpp:542
int spi_mode_pin
Definition tmc51x0_comm_interface.hpp:560
TMC51x0PinConfig()=default
Default constructor - all pins unmapped (-1)
int dc_in_pin
Definition tmc51x0_comm_interface.hpp:545
int sd_mode_pin
hardwired. HIGH=External step/dir, LOW=Internal ramp
Definition tmc51x0_comm_interface.hpp:562
int ref_right_pin
Right reference switch (REFR_DIR, pin 18) - Same as dir_pin.
Definition tmc51x0_comm_interface.hpp:532
int diag0_pin
DIAG0 pin (DIAG0_SWN, pin 26) - Optional.
Definition tmc51x0_comm_interface.hpp:536
int step_pin
STEP pin (REFL_STEP, pin 17) - Optional, same as ref_left_pin.
Definition tmc51x0_comm_interface.hpp:526
int dir_pin
DIR pin (REFR_DIR, pin 18) - Optional, same as ref_right_pin.
Definition tmc51x0_comm_interface.hpp:524
TMC51x0PinConfig(int en, int dir=-1, int step=-1) noexcept
Constructor with basic pins.
Definition tmc51x0_comm_interface.hpp:576
int enc_a_pin
Encoder A (ENCA_DCIN_CFG5, pin 24) - Same as dc_in_pin.
Definition tmc51x0_comm_interface.hpp:540
TMC5160 UART command/response frame structure with built-in CRC8.
Definition tmc51x0_comm_interface.hpp:890
bool IsWrite() const noexcept
Check if this is a write frame.
Definition tmc51x0_comm_interface.hpp:952
static UartFrame Write(uint8_t node_addr, uint8_t reg_addr, uint32_t value) noexcept
Construct a write access frame (8 bytes)
Definition tmc51x0_comm_interface.hpp:1037
static UartFrame ReadReply(const uint8_t *bytes) noexcept
Construct a read reply frame from received bytes (8 bytes)
Definition tmc51x0_comm_interface.hpp:1097
uint8_t GetAddress() const noexcept
Get register address from frame.
Definition tmc51x0_comm_interface.hpp:939
bool VerifyCrc() const noexcept
Verify CRC8 checksum of the frame.
Definition tmc51x0_comm_interface.hpp:990
size_t GetSize() const noexcept
Get frame size in bytes based on type.
Definition tmc51x0_comm_interface.hpp:931
void CalculateCrc() noexcept
Calculate and set CRC8 checksum for the frame CRC8 is calculated over all bytes except the CRC byte i...
Definition tmc51x0_comm_interface.hpp:975
union tmc51x0::UartFrame::Frame frame
void GetFrame(uint8_t *bytes) const noexcept
Get the frame as raw bytes.
Definition tmc51x0_comm_interface.hpp:1023
UartFrameType type
Frame type.
Definition tmc51x0_comm_interface.hpp:925
uint32_t GetValue() const noexcept
Get 32-bit data value from frame.
Definition tmc51x0_comm_interface.hpp:960
static UartFrame ReadRequest(uint8_t node_addr, uint8_t reg_addr) noexcept
Construct a read request frame (4 bytes)
Definition tmc51x0_comm_interface.hpp:1073
bool IsValid() const noexcept
Check if frame is valid.
Definition tmc51x0_comm_interface.hpp:1108
void SetFrame(const uint8_t *bytes, UartFrameType frame_type) noexcept
Set the frame from raw bytes.
Definition tmc51x0_comm_interface.hpp:1005
#define TMC51X0_SPI_MAX_CHAIN_DEVICES
Definition tmc51x0_comm_interface.hpp:1672
#define TMC51X0_LOG_DEBUG(comm_obj, level, tag,...)
Definition tmc51x0_comm_interface.hpp:191
Result type for error handling in TMC51x0 driver.
Union for accessing the 40-bit SPI frame in different ways.
Definition tmc51x0_comm_interface.hpp:724
struct tmc51x0::SpiCommand::Frame::@23 fields
Frame as structured fields.
uint64_t raw
Definition tmc51x0_comm_interface.hpp:731
uint8_t bytes[5]
Frame as 5 bytes (for direct byte access)
Definition tmc51x0_comm_interface.hpp:725
uint8_t address_byte
Address byte (bit 7 = write, bits 6-0 = address)
Definition tmc51x0_comm_interface.hpp:728
uint8_t data_bytes[4]
Data bytes (MSB to LSB)
Definition tmc51x0_comm_interface.hpp:729
Union for accessing UART frames in different ways.
Definition tmc51x0_comm_interface.hpp:894
uint8_t reg_addr
Byte 2: Register Address (0x00)
Definition tmc51x0_comm_interface.hpp:918
uint8_t data_bytes[4]
Bytes 3-6: 32-bit data (MSB-first)
Definition tmc51x0_comm_interface.hpp:902
struct tmc51x0::UartFrame::Frame::@26 read_reply_fields
struct tmc51x0::UartFrame::Frame::@24 write_fields
uint8_t node_addr
Byte 1: Node Address.
Definition tmc51x0_comm_interface.hpp:900
uint8_t bytes[8]
Frame as 8 bytes (maximum size)
Definition tmc51x0_comm_interface.hpp:895
uint8_t sync_reserved
Byte 0: Sync (0x05)
Definition tmc51x0_comm_interface.hpp:899
uint8_t master_addr
Byte 1: Master Address (0xFF)
Definition tmc51x0_comm_interface.hpp:917
uint8_t rw_address
Byte 2: RW bit (1) + 7-bit register address.
Definition tmc51x0_comm_interface.hpp:901
struct tmc51x0::UartFrame::Frame::@25 read_request_fields
uint8_t crc
Byte 7: CRC8 checksum.
Definition tmc51x0_comm_interface.hpp:903