6#ifndef TMC9660_BOOTLOADER_IMPL
7#define TMC9660_BOOTLOADER_IMPL
9#ifdef TMC9660_BOOTLOADER_HEADER_INCLUDED
50template <
typename CommType>
52 : comm_(comm), deviceAddr_(1), hostAddr_(255) {
68template <
typename CommType>
72 if constexpr (std::is_base_of_v<SpiCommInterface<CommType>,
CommType>) {
73 if (comm_.mode() == CommMode::SPI) {
74 return sendCommandSPI(
cmd, value,
reply);
77 if constexpr (std::is_base_of_v<UartCommInterface<CommType>,
CommType>) {
78 if (comm_.mode() == CommMode::UART) {
79 return sendCommandUART(
cmd, value,
reply);
82 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Unsupported interface mode for bootloader");
100template <
typename CommType>
102 if (comm_.mode() != CommMode::SPI) {
115 "Sending SPI bootloader command: cmd=0x%02X, value=0x%08X",
cmd, value);
129 if (!comm_.spiTransferBootloader(
txBuf,
rxBuf)) {
130 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to send SPI command (cmd=0x%02X)",
141 std::array<uint8_t, 5>
dummyTx = {
142 static_cast<uint8_t>(BootloaderCommand::NO_OP),
147 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"[BL TX NOOP] %02X %02X %02X %02X %02X",
152 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to receive SPI reply (cmd=0x%02X)",
157 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"[BL RX RPLY] %02X %02X %02X %02X %02X",
165 "SPI command failed: status=0x%02X (cmd=0x%02X)",
rep.status,
cmd);
173 "SPI command successful: status=0x%02X, value=0x%08X (cmd=0x%02X)",
rep.status,
190template <
typename CommType>
192 if (comm_.mode() != CommMode::UART) {
201 "Sending UART bootloader command: cmd=0x%02X, value=0x%08X",
cmd, value);
215 if (!comm_.uartTransferBootloader(
txBuf,
rxBuf)) {
216 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to send UART command (cmd=0x%02X)",
237 "UART command failed: status=0x%02X (cmd=0x%02X)",
rep.status,
cmd);
245 "UART command successful: status=0x%02X, value=0x%08X (cmd=0x%02X)",
rep.status,
263template <
typename CommType>
265 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::SET_BANK),
bank);
277template <
typename CommType>
279 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::SET_ADDRESS),
addr);
291template <
typename CommType>
293 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::WRITE_8),
v);
305template <
typename CommType>
307 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::WRITE_16),
v);
319template <
typename CommType>
321 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::WRITE_32),
v);
324template <
typename CommType>
327 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::WRITE_8_INC),
v);
330template <
typename CommType>
333 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::WRITE_16_INC),
v);
336template <
typename CommType>
339 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::WRITE_32_INC),
v);
353template <
typename CommType>
355 for (
size_t i = 0;
i <
count; ++
i) {
356 if (!write32Inc(
vals[
i]))
362template <
typename CommType>
365 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::NO_OP), 0,
reply);
384template <
typename CommType>
387 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"otpLoad: null result pointer");
392 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::OTP_LOAD),
page, &
reply)) {
400template <
typename CommType>
409 *errorCount =
result.errorCount;
411 *pageTag =
result.pageTag;
430template <
typename CommType>
433 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"otpBurn: null result pointer");
440 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::OTP_BURN), value, &
reply)) {
457template <
typename CommType>
497template <
typename CommType>
501 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"otpBurnWithWorkaround: null result pointer");
506 "Starting OTP burn with Erratum 1 workaround (page=%d, addr=0x%02X)",
page,
518 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 2: Setting address to 0x4801B010");
519 if (!setAddress(0x4801B010)) {
520 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set address 0x4801B010");
526 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 3: Reading 32-bit value");
535 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 4: Clearing bit 0 (read=0x%08X)",
540 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 5: Writing modified value 0x%08X",
543 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write modified value");
550 "Step 6: Waiting %dms for VDRV voltage to drop below 8.4V",
vdrvWaitMs);
554 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 7: Sending OTP_BURN command");
558 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::OTP_BURN), value, &
reply)) {
559 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to send OTP_BURN command");
566 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"OTP burn command completed successfully");
570 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"OTP burn command failed with error code: %d",
578template <
typename CommType>
582 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"checkOtpBurnStatus: null result pointer");
587 "Checking OTP burn status using Erratum 1 workaround");
591 "Step 1: Configuring clock for 15MHz system clock");
598 if (!setAddress(0x00020018)) {
599 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set clock config address");
605 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to read clock configuration");
613 "Failed to write modified clock configuration");
628 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 3: Setting address to 0x48020014");
629 if (!setAddress(0x48020014)) {
630 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set address 0x48020014");
635 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 4: Reading 16-bit value");
644 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"OTP burn status check: read=0x%04X, success=%s",
648 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Step 6: Restoring clock to 40MHz");
650 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set CONFIG bank for clock restore");
654 if (!setAddress(0x00020018)) {
656 "Failed to set clock config address for restore");
662 "Failed to restore original clock configuration");
676template <
typename CommType>
680 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_CONFIGURED),
690template <
typename CommType>
694 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_CONNECTED),
704template <
typename CommType>
708 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_BUSY),
722template <
typename CommType>
727 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::FLASH_SEND_CMD), value);
730template <
typename CommType>
736 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::FLASH_SEND_CMD), value, &
reply))
745template <
typename CommType>
750 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::FLASH_SEND_CMD), value);
753template <
typename CommType>
756 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::FLASH_ERASE_SECTOR),
757 address & 0x00FFFFFF);
760template <
typename CommType>
765 if (!flashLoadBuffer(0, 0x009F0000))
769 if (!flashSendDatagram(2))
774 if (!flashReadBuffer(0, &
data))
787template <
typename CommType>
792 (
static_cast<uint32_t>(hostAddr) << 16) |
795 bool result = sendCommand(
static_cast<uint8_t>(BootloaderCommand::BOOTSTRAP_RS485), value);
799 deviceAddr_ = deviceAddr;
800 hostAddr_ = hostAddr;
821template <
typename CommType>
823 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::GET_INFO),
836template <
typename CommType>
839 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"getBootloaderVersion: null pointer");
844 if (!getInfo(InfoQuery::BL_VERSION, &value)) {
852template <
typename CommType>
861 if (!getInfo(InfoQuery::FEATURES, &value)) {
869template <
typename CommType>
878 if (!getInfo(InfoQuery::GIT_INFO, &value)) {
886template <
typename CommType>
890 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"getPartitionVersion: null pointer");
895 if (!getInfo(InfoQuery::PARTITION_VERSION, &value)) {
955template <
typename CommType>
958 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Starting bootloader configuration application");
964 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Successfully set bank 5 (CONFIG memory bank)");
969 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set LDO config register");
974 ldo |= (
static_cast<uint16_t>(
cfg.ldo.vext2) & 0x3) << 2;
975 ldo |= (
static_cast<uint16_t>(
cfg.ldo.slope_vext1) & 0x3) << 4;
976 ldo |= (
static_cast<uint16_t>(
cfg.ldo.slope_vext2) & 0x3) << 6;
977 ldo |= (
cfg.ldo.ldo_short_fault ? 1u : 0
u) << 8;
979 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write LDO config (0x%04X)", ldo);
982 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"LDO configured (VEXT1: %d, VEXT2: %d)",
983 static_cast<int>(
cfg.ldo.vext1),
static_cast<int>(
cfg.ldo.vext2));
986 if (!readAndVerify16(ldo,
"LDO config")) {
988 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"LDO configuration verification failed");
992 "⚠️ LDO configuration verification failed (continuing)");
1000 "Configuring clock settings (FINAL STEP BEFORE MOTOR CONTROL)");
1002 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set clock config register");
1007 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Reading current clock configuration...");
1011 "⚠️ Failed to read current clock config (continuing)");
1013 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Current clock config: 0x%08X (PLL_STATUS=%s)",
1019 clk |= (
static_cast<uint32_t>(
cfg.clock.use_external) & 0x1) << 8;
1021 clk |= (
cfg.clock.xtal_boost ? 1u : 0
u) << 12;
1022 clk |= (
static_cast<uint32_t>(
cfg.clock.ext_source_type) & 0x1) << 13;
1023 clk |= (
static_cast<uint32_t>(
cfg.clock.pll_selection) & 0x3) << 16;
1024 clk |= (
cfg.clock.rdiv & 0x1F) << 18;
1025 clk |= (
static_cast<uint32_t>(
cfg.clock.sysclk_div) & 0x3) << 23;
1026 if (!write32(
clk)) {
1027 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write clock config (0x%08X)",
clk);
1031 "Clock configuration written (external: %s, xtal_drive: %d, rdiv: %d)",
1032 cfg.clock.use_external == bootcfg::ClockSource::External ?
"yes" :
"no",
1033 static_cast<int>(
cfg.clock.xtal_drive),
cfg.clock.rdiv);
1039 "Waiting for clock reconfiguration to complete...");
1045 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Clock reconfiguration delay completed");
1050 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to read back clock config");
1054 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"⚠️ Clock config read failed (continuing)");
1061 "❌ PLL_STATUS not set after clock reconfiguration: 0x%08X",
actual_clock);
1063 " Clock reconfiguration may have failed or needs more time");
1067 "⚠️ PLL_STATUS not set after clock reconfiguration: 0x%08X (continuing)",
1071 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"✅ PLL_STATUS confirmed SET: 0x%08X",
1082 "❌ Clock config verification failed: expected=0x%08X, actual=0x%08X",
1085 " Full actual value: 0x%08X (PLL_STATUS=%s)",
actual_clock,
1090 comm_, 1,
"TMC9660Bootloader",
1091 "⚠️ Clock config verification failed: expected=0x%08X, actual=0x%08X (continuing)",
1096 "✅ Clock config verified: 0x%08X (PLL_STATUS=%s)",
actual_clock,
1104 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set UART address register");
1108 (
static_cast<uint16_t>(
cfg.uart.host_address) << 8);
1110 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write UART address word (0x%04X)",
1115 "UART addresses configured (device: %d, host: %d)",
cfg.uart.device_address,
1116 cfg.uart.host_address);
1119 if (!readAndVerify16(
addr_word,
"UART addresses")) {
1121 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"UART addresses verification failed");
1125 "⚠️ UART addresses verification failed (continuing)");
1132 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set RS485 delay register");
1136 (
static_cast<uint16_t>(
cfg.rs485.txen_pre_delay) << 8);
1137 if (!write16(rs485)) {
1138 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write RS485 delay config (0x%08X)",
1142 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"RS485 delays configured (pre: %d, post: %d)",
1143 cfg.rs485.txen_pre_delay,
cfg.rs485.txen_post_delay);
1146 if (!readAndVerify16(rs485,
"RS485 delays")) {
1148 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"RS485 delays verification failed");
1152 "⚠️ RS485 delays verification failed (continuing)");
1158 "Configuring communication settings (shared register with SPI flash)");
1160 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set communication config register");
1167 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Current COMM_CONFIG before overwrite: 0x%04X",
1169 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bit 0 (UART disabled): %d",
1171 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bit 1 (SPI disabled): %d",
1173 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bit 2 (SPI SELECT): %d",
1175 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bit 3 (UART RX): %d",
1177 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bit 4 (UART TX): %d",
1179 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bits 5-6 (UART TXEN): %d",
1181 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bits 7-9 (UART BAUD): %d",
1183 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" Current Bit 10 (SPI0 SCK): %d",
1187 "⚠️ Failed to read current COMM_CONFIG (continuing)");
1194 comm |= (
cfg.uart.disable_uart ? 1u : 0
u) << 0;
1195 comm |= (
static_cast<uint16_t>(
cfg.uart.rx_pin) & 0x1)
1197 comm |= (
static_cast<uint16_t>(
cfg.uart.tx_pin) & 0x1)
1199 comm |= (
static_cast<uint16_t>(
cfg.rs485.txen_pin) & 0x3) << 5;
1200 comm |= (
static_cast<uint16_t>(
cfg.uart.baud_rate) & 0x7) << 7;
1203 comm |= (
cfg.spiComm.disable_spi ? 1u : 0
u) << 1;
1225 if (!
cfg.spiComm.disable_spi &&
cfg.spiFlash.enable_flash) {
1229 " Both bootloader and flash are configured for %s!",
1232 " Per datasheet: they MUST be on separate interfaces");
1235 comm_, 0,
"TMC9660Bootloader",
" - Bootloader SPI: %s (disable_spi=%d)",
1237 cfg.spiComm.disable_spi ? 1 : 0);
1239 comm_, 0,
"TMC9660Bootloader",
" - Flash SPI: %s (enable_flash=%d)",
1241 cfg.spiFlash.enable_flash ? 1 : 0);
1243 " Fix: Set one to SPI0 and the other to SPI1");
1249 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
" - Bootloader: %s (enabled=%d)",
1264 if (!
cfg.spiComm.disable_spi) {
1268 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
" - BL_SPI_SELECT=%d (bootloader on %s)",
1270 }
else if (
cfg.spiFlash.enable_flash) {
1287 comm |= (
static_cast<uint16_t>(
cfg.spiComm.spi0_sck_pin) & 0x1)
1290 "BL_SPI0_SCK set from bootloader config (SPI0_SCK pin: %d)",
1291 static_cast<int>(
cfg.spiComm.spi0_sck_pin));
1294 comm |= (
static_cast<uint32_t>(
cfg.spiFlash.spi0_sck_pin) & 0x1) << 10;
1296 "BL_SPI0_SCK set from flash config (SPI0_SCK pin: %d)",
1297 static_cast<int>(
cfg.spiFlash.spi0_sck_pin));
1302 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"NEW COMM_CONFIG to be written: 0x%04X", comm);
1303 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" New Bit 0 (UART disabled): %d",
1305 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" New Bit 1 (SPI disabled): %d",
1309 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" New Bit 3 (UART RX): %d", (comm >> 3) & 0x1);
1310 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" New Bit 4 (UART TX): %d", (comm >> 4) & 0x1);
1311 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" New Bits 5-6 (UART TXEN): %d",
1313 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
" New Bits 7-9 (UART BAUD): %d (expected: %d)",
1314 (comm >> 7) & 0x7,
static_cast<int>(
cfg.uart.baud_rate));
1316 (comm >> 10) & 0x1);
1318 if (!write16(comm)) {
1320 "Failed to write communication config (0x%04X)", comm);
1323 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Communication config written (0x%04X):", comm);
1325 " UART: disabled=%s, RX=GPIO%d, TX=GPIO%d, TXEN=%s, baud=%d",
1326 cfg.uart.disable_uart ?
"yes" :
"no",
1332 static_cast<int>(
cfg.uart.baud_rate));
1334 comm_, 3,
"TMC9660Bootloader",
1335 " SPI: disabled=%s, bootloader_SPI0=%s, flash_SPI0=%s, SPI0_SCK=GPIO%d",
1345 if (!readAndVerify16(comm,
"Communication config")) {
1347 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Communication config verification failed");
1351 "⚠️ Communication config verification failed (continuing)");
1374 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"Configuring SPI flash settings");
1376 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set SPI flash register");
1397 if (!write16(
flash)) {
1398 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write SPI flash config (0x%04X)",
1412 cfg.spiFlash.enable_flash ? 1 : 0);
1414 " - SPI_IFACE_SELECT (bit 1): %d (mirrors COMM_CONFIG bit 2, %s)",
1417 " - SPI0_SCK_SELECT (bit 2): %d (mirrors COMM_CONFIG bit 10, %s)",
sck_pin_bit,
1419 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
" - CS_PIN (bits 3-7): GPIO%d",
1420 cfg.spiFlash.cs_pin);
1421 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
" - FREQ_DIV (bits 8-11): Div%d",
1422 1 <<
static_cast<int>(
cfg.spiFlash.freq_div));
1426 if (comm_.mode() == CommMode::UART) {
1429 "Failed to reset SPI flash register address for verification");
1435 if (!readAndVerify16(
flash,
"SPI flash config")) {
1437 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"SPI flash config verification failed");
1441 "⚠️ SPI flash config verification failed (continuing)");
1446 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring I2C EEPROM settings");
1448 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set I2C config register");
1452 i2c |= (
static_cast<uint16_t>(
cfg.i2c.sda_pin) & 0x3) << 1;
1453 i2c |= (
static_cast<uint16_t>(
cfg.i2c.scl_pin) & 0x3) << 3;
1454 i2c |= (
static_cast<uint16_t>(
cfg.i2c.address_bits & 0x7)) << 5;
1455 i2c |= (
static_cast<uint16_t>(
cfg.i2c.freq_code) & 0x7) << 8;
1456 if (!write16(i2c)) {
1457 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write I2C config (0x%04X)", i2c);
1461 "I2C EEPROM configured (enabled: %s, SDA: %d, SCL: %d, addr_bits: %d)",
1462 cfg.i2c.enable_eeprom ?
"yes" :
"no",
static_cast<int>(
cfg.i2c.sda_pin),
1463 static_cast<int>(
cfg.i2c.scl_pin),
cfg.i2c.address_bits);
1467 if (comm_.mode() == CommMode::UART) {
1470 "Failed to reset I2C config register address for verification");
1476 if (!readAndVerify16(i2c,
"I2C config")) {
1478 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"I2C config verification failed");
1482 "⚠️ I2C config verification failed (continuing)");
1490 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring GPIO output levels (GPIOs 0-15)");
1492 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set GPIO output register");
1495 if (!write16(
cfg.gpio.outputMask_0_15)) {
1496 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write GPIO output mask (0x%04X)",
1497 cfg.gpio.outputMask_0_15);
1501 "GPIO output levels configured (GPIOs 0-15: 0x%04X)",
cfg.gpio.outputMask_0_15);
1504 if (!readAndVerify16(
cfg.gpio.outputMask_0_15,
"GPIO output levels")) {
1506 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"GPIO output levels verification failed");
1510 "⚠️ GPIO output levels verification failed (continuing)");
1515 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring GPIO direction (GPIOs 0-15)");
1517 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set GPIO direction register");
1520 if (!write16(
cfg.gpio.directionMask_0_15)) {
1521 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write GPIO direction mask (0x%04X)",
1522 cfg.gpio.directionMask_0_15);
1525 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"GPIO direction configured (GPIOs 0-15: 0x%04X)",
1526 cfg.gpio.directionMask_0_15);
1529 if (!readAndVerify16(
cfg.gpio.directionMask_0_15,
"GPIO direction")) {
1531 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"GPIO direction verification failed");
1535 "⚠️ GPIO direction verification failed (continuing)");
1540 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring GPIO pull-up (GPIOs 0-15)");
1542 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set GPIO pull-up register");
1545 if (!write16(
cfg.gpio.pullUpMask_0_15)) {
1546 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write GPIO pull-up mask (0x%04X)",
1547 cfg.gpio.pullUpMask_0_15);
1550 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"GPIO pull-up configured (GPIOs 0-15: 0x%04X)",
1551 cfg.gpio.pullUpMask_0_15);
1554 if (!readAndVerify16(
cfg.gpio.pullUpMask_0_15,
"GPIO pull-up")) {
1556 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"GPIO pull-up verification failed");
1560 "⚠️ GPIO pull-up verification failed (continuing)");
1565 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring GPIO pull-down (GPIOs 0-15)");
1567 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set GPIO pull-down register");
1570 if (!write16(
cfg.gpio.pullDownMask_0_15)) {
1571 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write GPIO pull-down mask (0x%04X)",
1572 cfg.gpio.pullDownMask_0_15);
1575 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"GPIO pull-down configured (GPIOs 0-15: 0x%04X)",
1576 cfg.gpio.pullDownMask_0_15);
1579 if (!readAndVerify16(
cfg.gpio.pullDownMask_0_15,
"GPIO pull-down")) {
1581 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"GPIO pull-down verification failed");
1585 "⚠️ GPIO pull-down verification failed (continuing)");
1591 "Configuring GPIO extended settings (GPIOs 16-18 + analog 2-5)");
1593 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set GPIO extended register");
1598 gpio_ext |= (
cfg.gpio.directionMask_16_18 & 0x7) << 3;
1599 gpio_ext |= (
cfg.gpio.pullDownMask_16_18 & 0x7) << 6;
1600 gpio_ext |= (
cfg.gpio.pullUpMask_16_18 & 0x7) << 9;
1601 gpio_ext |= (
cfg.gpio.analogMask_2_5 & 0xF) << 12;
1604 "Failed to write GPIO extended config (0x%04X)",
gpio_ext);
1608 "GPIO extended configured (output_16_18:0x%X, dir_16_18:0x%X, pd_16_18:0x%X, "
1609 "pu_16_18:0x%X, analog_2_5:0x%X)",
1610 cfg.gpio.outputMask_16_18,
cfg.gpio.directionMask_16_18,
1611 cfg.gpio.pullDownMask_16_18,
cfg.gpio.pullUpMask_16_18,
1612 cfg.gpio.analogMask_2_5);
1615 if (!readAndVerify16(
gpio_ext,
"GPIO extended config")) {
1617 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"GPIO extended config verification failed");
1621 "⚠️ GPIO extended config verification failed (continuing)");
1625 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"GPIO configuration completed");
1629 "Configuring Hall encoder and ABN encoder 1 settings");
1631 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set Hall/ABN1 config register");
1646 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write Hall/ABN1 config (0x%08X)",
1651 "Hall/ABN1 configured - Hall(enabled:%s, U:%d, V:%d, W:%d), ABN1(enabled:%s, "
1652 "A:%d, B:%d, N:%d)",
1653 cfg.hall.enable ?
"yes" :
"no",
static_cast<int>(
cfg.hall.u_pin),
1654 static_cast<int>(
cfg.hall.v_pin),
static_cast<int>(
cfg.hall.w_pin),
1655 cfg.abn1.enable ?
"yes" :
"no",
static_cast<int>(
cfg.abn1.a_pin),
1656 static_cast<int>(
cfg.abn1.b_pin),
static_cast<int>(
cfg.abn1.n_pin));
1659 if (!readAndVerify16(
hall_abn1,
"Hall/ABN1 config")) {
1661 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Hall/ABN1 config verification failed");
1665 "⚠️ Hall/ABN1 config verification failed (continuing)");
1671 "Configuring ABN2, REF switches, and Step/Dir settings");
1674 "Failed to set ABN2/REF/StepDir config register");
1697 comm_, 3,
"TMC9660Bootloader",
1698 "ABN2/REF/StepDir configured (ABN2:%s, REF_L:%d, REF_R:%d, REF_H:%d, StepDir:%s)",
1699 cfg.abn2.enable ?
"enabled" :
"disabled",
static_cast<int>(
cfg.ref.ref_l_pin),
1700 static_cast<int>(
cfg.ref.ref_r_pin),
static_cast<int>(
cfg.ref.ref_h_pin),
1701 cfg.stepDir.enable ?
"enabled" :
"disabled");
1707 "ABN2/REF/StepDir config verification failed");
1711 "⚠️ ABN2/REF/StepDir config verification failed (continuing)");
1717 "Configuring mechanical brake and brake chopper settings");
1719 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set brake config register");
1724 brake |= (
cfg.brakeChopper.enable ? 1u : 0
u) << 4;
1728 brake |= (
cfg.mechBrake.enable ? 1u : 0
u) << 12;
1731 if (!write16(
brake)) {
1732 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write brake config (0x%04X)",
1737 "Brake configuration completed (mech_brake:%s, brake_chopper:%s)",
1738 cfg.mechBrake.enable ?
"enabled" :
"disabled",
1739 cfg.brakeChopper.enable ?
"enabled" :
"disabled");
1742 if (!readAndVerify16(
brake,
"Brake config")) {
1744 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Brake config verification failed");
1748 "⚠️ Brake config verification failed (continuing)");
1753 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring SPI encoder settings");
1755 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set SPI encoder config register");
1759 spiEnc |= (
static_cast<uint16_t>(
cfg.spiEnc.spi_block) & 0x1) << 1;
1760 spiEnc |= (
static_cast<uint16_t>(
cfg.spiEnc.spi_mode) & 0x3) << 2;
1761 spiEnc |= (
static_cast<uint16_t>(
cfg.spiEnc.spi_freq) & 0xF) << 4;
1762 spiEnc |= (
static_cast<uint16_t>(
cfg.spiEnc.cs_pin) & 0x3) << 8;
1763 spiEnc |= (
static_cast<uint16_t>(
cfg.spiEnc.cs_polarity) & 0x1) << 10;
1764 if (!write16(spiEnc)) {
1765 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write SPI encoder config (0x%04X)",
1770 "SPI encoder configured (enabled: %s, block:%d, mode:%d, freq:%d)",
1771 cfg.spiEnc.enable ?
"yes" :
"no",
static_cast<int>(
cfg.spiEnc.spi_block),
1772 static_cast<int>(
cfg.spiEnc.spi_mode),
static_cast<int>(
cfg.spiEnc.spi_freq));
1775 if (!readAndVerify16(spiEnc,
"SPI encoder config")) {
1777 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"SPI encoder config verification failed");
1781 "⚠️ SPI encoder config verification failed (continuing)");
1786 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring external memory storage settings");
1789 "Failed to set memory storage config register");
1793 static_cast<uint8_t>(
cfg.memStorage.tmcl_script) & 0x3;
1798 "Failed to write memory storage config (0x%02X)",
mem_storage);
1802 comm_, 3,
"TMC9660Bootloader",
"Memory storage configured (TMCL_script:%d, parameters:%d)",
1803 static_cast<int>(
cfg.memStorage.tmcl_script),
static_cast<int>(
cfg.memStorage.parameters));
1806 if (!readAndVerify8(
mem_storage,
"Memory storage config")) {
1808 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Memory storage config verification failed");
1812 "⚠️ Memory storage config verification failed (continuing)");
1821 if (
cfg.spiFlash.enable_flash) {
1822 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"========================================");
1823 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"Verifying SPI Flash Memory Configuration");
1824 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"========================================");
1827 cfg.spiFlash.enable_flash ?
"ENABLED" :
"DISABLED");
1828 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
" - CS Pin: GPIO%d (bits 3-7)",
1829 cfg.spiFlash.cs_pin);
1830 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
" - Frequency: Div%d (bits 8-11)",
1831 1 <<
static_cast<int>(
cfg.spiFlash.freq_div));
1833 comm_, 2,
"TMC9660Bootloader",
" - SPI Interface: %s (bit 12)",
1836 comm_, 2,
"TMC9660Bootloader",
" - SCK Pin: %s (bit 13)",
1842 "Checking MEM_IS_CONFIGURED (bank=1, SPI Flash)...");
1843 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_CONFIGURED),
1848 "✅ SPI Flash is CONFIGURED (bootloader sees SPI_FLASH_EN=1)");
1853 "Checking MEM_IS_CONNECTED (probing flash chip)...");
1854 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_CONNECTED),
1860 "✅ SPI Flash is CONNECTED (chip responded to probe)");
1865 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_BUSY),
1870 "✅ SPI Flash is READY (not busy)");
1873 "⚠️ SPI Flash is BUSY (write in progress)");
1877 "⚠️ Failed to check SPI Flash busy status");
1883 "Querying partition count (GET_INFO SPI_MEM_PARTITIONS)...");
1884 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::GET_INFO),
1885 static_cast<uint32_t>(InfoQuery::SPI_MEM_PARTITIONS),
1887 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"SPI Flash partition count: %u",
1890 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"⚠️ SPI Flash has NO partitions");
1892 comm_, 1,
"TMC9660Bootloader",
1893 " Flash is connected but not partitioned (no partition table)");
1895 " See datasheet Table 21 for partition header format");
1897 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"✅ SPI Flash has %u partition(s)",
1902 "⚠️ Failed to query SPI Flash partition count");
1905 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"⚠️ SPI Flash is NOT CONNECTED");
1908 " 1. No flash chip physically connected");
1910 " 2. Wrong CS pin configured (expected GPIO%d)",
1911 cfg.spiFlash.cs_pin);
1913 comm_, 1,
"TMC9660Bootloader",
" 3. Wrong SPI interface (configured: %s)",
1917 " 4. Flash chip not responding to probe (0xAB, 0x90, 0x9F)");
1921 "⚠️ Failed to send MEM_IS_CONNECTED command");
1924 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"⚠️ SPI Flash is NOT CONFIGURED");
1926 " Bootloader reports SPI_FLASH_EN=0 (bit 0 of flash config)");
1928 " This means the config write may have failed or been overwritten");
1930 " Check that cfg.spiFlash.enable_flash = true");
1934 "⚠️ Failed to send MEM_IS_CONFIGURED command");
1936 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"========================================");
1940 if (
cfg.i2c.enable_eeprom) {
1941 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"Verifying I2C EEPROM memory...");
1945 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_CONFIGURED),
1952 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_CONNECTED),
1959 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::MEM_IS_BUSY),
1963 "✅ I2C EEPROM is READY (not busy)");
1969 "⚠️ Failed to check I2C EEPROM busy status");
1974 if (sendCommand(
static_cast<uint8_t>(BootloaderCommand::GET_INFO),
1975 static_cast<uint32_t>(InfoQuery::I2C_MEM_PARTITIONS),
1977 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"I2C EEPROM partition count: %u",
1981 "⚠️ I2C EEPROM has NO partitions (not partitioned)");
1985 "⚠️ Failed to query I2C EEPROM partition count");
1989 "⚠️ I2C EEPROM is NOT CONNECTED (check wiring)");
1993 "⚠️ Failed to check I2C EEPROM connection status");
1997 "⚠️ I2C EEPROM is NOT CONFIGURED (check config settings)");
2001 "⚠️ Failed to check I2C EEPROM configuration status");
2008 TMC9660_LOG_DEBUG(comm_, 3,
"TMC9660Bootloader",
"Configuring boot settings (FINAL STEP)");
2010 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set boot config register");
2014 boot |=
static_cast<uint16_t>(
cfg.boot.boot_mode) & 0x3;
2015 boot |= (
cfg.boot.bl_ready_fault ? 1u : 0
u) << 2;
2016 boot |= (
cfg.boot.bl_exit_fault ? 1u : 0
u) << 3;
2017 boot |= (
cfg.boot.disable_selftest ? 1u : 0
u) << 8;
2018 boot |= (
cfg.boot.bl_config_fault ? 1u : 0
u) << 9;
2019 boot |= (
cfg.boot.start_motor_control ? 1u : 0
u) << 12;
2020 if (!write16(boot)) {
2021 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write boot config (0x%04X)", boot);
2025 "Boot config written: mode=%d, start_motor_ctrl=%s, bl_exit_fault=%s",
2026 static_cast<int>(
cfg.boot.boot_mode),
2027 cfg.boot.start_motor_control ?
"TRUE" :
"FALSE",
2028 cfg.boot.bl_exit_fault ?
"TRUE" :
"FALSE");
2043 if (
cfg.boot.start_motor_control) {
2045 "⚠️ START_MOTOR_CTRL=1 written - Bootloader will hopefully EXIT and motor "
2046 "control will START!");
2048 "⚠️ No further bootloader commands will be accepted after this point!");
2051 "START_MOTOR_CTRL=0 - Bootloader remains active, motor control NOT started");
2055 "Bootloader configuration application completed successfully");
2093template <
typename CommType>
2095 TMC9660_LOG_DEBUG(comm_, 2,
"TMC9660Bootloader",
"Starting motor control system (mode: %d)...",
2101 "Failed to set CONFIG bank before starting motor control");
2107 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to set BOOT_CONFIG address");
2124 "Writing BOOT_CONFIG=0x%08X (mode=%d, START_MOTOR_CTRL=1)",
boot_config,
2130 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Failed to write BOOT_CONFIG");
2135 "⚠️ START_MOTOR_CTRL command sent - Bootloader is EXITING NOW!");
2136 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"⚠️ Motor control system starting in %s mode",
2137 bootMode == bootcfg::BootMode::Parameter ?
"PARAMETER" :
"REGISTER");
2139 "⚠️ Allow 100-150ms for motor control to fully initialize");
2141 "⚠️ Bootloader commands will NO LONGER work after this point");
2159template <
typename CommType>
2167 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::READ_8), 0, &
reply)) {
2184template <
typename CommType>
2192 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::READ_16), 0, &
reply)) {
2209template <
typename CommType>
2216 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::READ_32), 0, value);
2219template <
typename CommType>
2228 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::READ_8_INC), 0, &
reply)) {
2236template <
typename CommType>
2245 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::READ_16_INC), 0, &
reply)) {
2253template <
typename CommType>
2261 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::READ_32_INC), 0, value);
2264template <
typename CommType>
2273 if (!sendCommand(
static_cast<uint8_t>(BootloaderCommand::GET_BANK), 0, &
reply)) {
2281template <
typename CommType>
2292 "❌ %s verification failed: expected=0x%02X, actual=0x%02X",
configName,
2301template <
typename CommType>
2312 "❌ %s verification failed: expected=0x%04X, actual=0x%04X",
configName,
2321template <
typename CommType>
2332 "❌ %s verification failed: expected=0x%08X, actual=0x%08X",
configName,
2341template <
typename CommType>
2349 return sendCommand(
static_cast<uint8_t>(BootloaderCommand::GET_ADDRESS), 0, address);
2352template <
typename CommType>
2357 "╔══════════════════════════════════════════════════════════════╗");
2359 "║ TMC9660 BOOTLOADER INFORMATION ║");
2361 "╚══════════════════════════════════════════════════════════════╝");
2367 if (getChipType(&value)) {
2369 (value == 0x544D0001) ?
"(TMC9660 ✓)" :
"(Unknown!)");
2377 if (getBootloaderVersion(&
blVer)) {
2381 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Bootloader Version: FAILED TO READ");
2385 if (getChipVersion(&value)) {
2386 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"Silicon Revision: %u", value);
2388 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Silicon Revision: FAILED TO READ");
2392 if (getChipVariant(&value)) {
2394 (value == 2) ?
"(TMC9660 ✓)" :
"(Unexpected!)");
2396 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"Chip Variant: FAILED TO READ");
2400 if (getChipFrequency(&value)) {
2401 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"System Frequency: %u MHz", value);
2403 TMC9660_LOG_DEBUG(comm_, 0,
"TMC9660Bootloader",
"System Frequency: FAILED TO READ");
2420 features.sram_support ?
"YES" :
"NO");
2426 features.spi_flash ?
"YES" :
"NO");
2428 features.i2c_eeprom ?
"YES" :
"NO");
2439 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"CONFIG Memory: Info not available");
2443 if (getOtpMemSize(&value)) {
2444 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"OTP Page Size: %u bytes", value);
2446 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"OTP Page Size: Not available");
2451 if (getPartitionVersion(&
partVer)) {
2455 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"Partition Version: Not available");
2462 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"SPI Flash Size: %u bytes (%.2f KB)",
2474 "SPI Partitions: Not available (may need partitioning)");
2481 "SPI Flash: Not connected or not configured");
2488 TMC9660_LOG_DEBUG(comm_, 1,
"TMC9660Bootloader",
"I2C EEPROM Size: %u bytes (%.2f KB)",
2500 "I2C Partitions: Not available (may need partitioning)");
2507 "I2C EEPROM: Not connected or not configured");
2511 "╚══════════════════════════════════════════════════════════════╝");
SPI status codes as per TMC9660 Parameter Mode.
Definition tmc9660_comm_interface.hpp:514
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
bool setAddress(uint32_t addr) noexcept
Set the address within the current memory bank.
Definition tmc9660_bootloader.cpp:278
bool flashReadBuffer(uint8_t offset, uint32_t *data) noexcept
Read data from internal 6-byte flash command buffer.
Definition tmc9660_bootloader.cpp:732
bool getInfo(InfoQuery query, uint32_t *value) noexcept
Get bootloader information.
Definition tmc9660_bootloader.cpp:822
bool getAddress(uint32_t *address) noexcept
Get the currently selected memory address.
Definition tmc9660_bootloader.cpp:2343
bool readAndVerify8(uint8_t expected, const char *config_name) noexcept
Read back and verify an 8-bit value matches the expected value.
Definition tmc9660_bootloader.cpp:2283
bool readAndVerify32(uint32_t expected, const char *config_name) noexcept
Read back and verify a 32-bit value matches the expected value.
Definition tmc9660_bootloader.cpp:2323
bool memIsBusy(MemoryBank bank, bool *is_busy) noexcept
Check if external memory is busy.
Definition tmc9660_bootloader.cpp:706
bool sendCommandSPI(uint8_t cmd, uint32_t value, uint32_t *reply) noexcept
Send a bootloader command via SPI interface.
Definition tmc9660_bootloader.cpp:101
bool sendCommand(uint8_t cmd, uint32_t value, uint32_t *reply=nullptr) noexcept
Send a bootloader command and optionally receive a reply.
Definition tmc9660_bootloader.cpp:69
bool flashEraseSector(uint32_t address) noexcept
Erase a sector on external SPI flash.
Definition tmc9660_bootloader.cpp:755
bool read16Inc(uint16_t *value) noexcept
Read a 16-bit word and increment address by 2.
Definition tmc9660_bootloader.cpp:2238
bool bootstrapRS485(uint8_t tx_en_pin, uint8_t pre_delay, uint8_t host_addr, uint8_t device_addr) noexcept
Configure RS485 communication (must be first command for RS485).
Definition tmc9660_bootloader.cpp:788
bool noOp(uint32_t *reply=nullptr) noexcept
No operation - retrieve reply from previous command (SPI only).
Definition tmc9660_bootloader.cpp:364
bool sendCommandUART(uint8_t cmd, uint32_t value, uint32_t *reply) noexcept
Send a bootloader command via UART interface.
Definition tmc9660_bootloader.cpp:191
bool startMotorControl(bootcfg::BootMode boot_mode=bootcfg::BootMode::Parameter) noexcept
Start the motor control system and exit bootloader mode.
Definition tmc9660_bootloader.cpp:2094
bool otpBurnWithWorkaround(uint8_t page, uint8_t page_addr, OtpBurnResult *result, uint32_t vdrv_wait_ms=1000) noexcept
Burn OTP page with Erratum 1 workaround for reliable operation.
Definition tmc9660_bootloader.cpp:498
bool read32Inc(uint32_t *value) noexcept
Read a 32-bit word and increment address by 4.
Definition tmc9660_bootloader.cpp:2255
bool readAndVerify16(uint16_t expected, const char *config_name) noexcept
Read back and verify a 16-bit value matches the expected value.
Definition tmc9660_bootloader.cpp:2303
bool getBank(uint8_t *bank) noexcept
Get the currently selected memory bank.
Definition tmc9660_bootloader.cpp:2266
bool write8Inc(uint8_t v) noexcept
Write a single byte and increment address by 1.
Definition tmc9660_bootloader.cpp:326
bool applyConfiguration(const BootloaderConfig &cfg, bool fail_on_verify_error=true) noexcept
Apply all fields of a ::BootloaderConfig.
Definition tmc9660_bootloader.cpp:956
bool memIsConfigured(MemoryBank bank, bool *is_configured) noexcept
Check if external memory is configured.
Definition tmc9660_bootloader.cpp:678
bool memIsConnected(MemoryBank bank, bool *is_connected) noexcept
Check if external memory is connected.
Definition tmc9660_bootloader.cpp:692
bool getGitInfo(GitInfo *git_info) noexcept
Get Git version control information.
Definition tmc9660_bootloader.cpp:871
bool write32(uint32_t v) noexcept
Write a 32-bit word to the previously selected address.
Definition tmc9660_bootloader.cpp:320
bool write16(uint16_t v) noexcept
Write a 16-bit word to the previously selected address.
Definition tmc9660_bootloader.cpp:306
bool checkOtpBurnStatus(bool *result) noexcept
Check OTP burn status using Erratum 1 workaround verification.
Definition tmc9660_bootloader.cpp:580
bool read8(uint8_t *value) noexcept
Read a single byte from the previously selected address.
Definition tmc9660_bootloader.cpp:2160
bool getFeatures(BootloaderFeatures *features) noexcept
Get available feature flags.
Definition tmc9660_bootloader.cpp:854
bool read32(uint32_t *value) noexcept
Read a 32-bit word from the previously selected address.
Definition tmc9660_bootloader.cpp:2210
TMC9660Bootloader(CommType &comm) noexcept
Construct a TMC9660Bootloader instance.
Definition tmc9660_bootloader.cpp:51
bool otpLoad(uint8_t page, OtpLoadResult *result) noexcept
Load an OTP (One-Time Programmable) page into the OTP memory bank.
Definition tmc9660_bootloader.cpp:385
bool flashLoadBuffer(uint8_t offset, uint32_t data) noexcept
Load data into internal 6-byte flash command buffer.
Definition tmc9660_bootloader.cpp:724
bool flashReadJedecId(uint8_t *manufacturer_id) noexcept
Read JEDEC manufacturer ID from SPI flash.
Definition tmc9660_bootloader.cpp:762
bool setBank(uint8_t bank) noexcept
Select the target memory bank for subsequent operations.
Definition tmc9660_bootloader.cpp:264
bool write32Inc(uint32_t v) noexcept
Write a 32-bit word and increment address by 4.
Definition tmc9660_bootloader.cpp:338
bool read16(uint16_t *value) noexcept
Read a 16-bit word from the previously selected address.
Definition tmc9660_bootloader.cpp:2185
bool write32IncMultiple(const uint32_t *values, size_t count) noexcept
Write multiple 32-bit words starting at the current address.
Definition tmc9660_bootloader.cpp:354
bool write8(uint8_t v) noexcept
Write a single byte to the previously selected address.
Definition tmc9660_bootloader.cpp:292
bool flashSendDatagram(uint8_t num_bytes) noexcept
Send datagram to SPI flash and receive reply.
Definition tmc9660_bootloader.cpp:747
bool read8Inc(uint8_t *value) noexcept
Read a single byte and increment address by 1.
Definition tmc9660_bootloader.cpp:2221
bool getAllBootloaderInfo() noexcept
Retrieve and log all available bootloader information.
Definition tmc9660_bootloader.cpp:2354
bool write16Inc(uint16_t v) noexcept
Write a 16-bit word and increment address by 2.
Definition tmc9660_bootloader.cpp:332
bool getBootloaderVersion(BootloaderVersion *version) noexcept
Get bootloader version information.
Definition tmc9660_bootloader.cpp:837
bool otpBurn(uint8_t page, uint8_t page_addr, OtpBurnResult *result) noexcept
Permanently burn data to OTP (One-Time Programmable) memory.
Definition tmc9660_bootloader.cpp:431
bool getPartitionVersion(PartitionVersion *version) noexcept
Get partition version information.
Definition tmc9660_bootloader.cpp:888
constexpr uint32_t MEM_STORAGE_CONFIG
External memory storage selection register.
Definition bootloader_config.hpp:734
constexpr uint32_t UART_ADDR
UART device/host address register.
Definition bootloader_config.hpp:572
constexpr uint32_t MECH_BRAKE_CONFIG
Mechanical brake configuration register.
Definition bootloader_config.hpp:718
constexpr uint32_t GPIO_DIR
GPIO direction register.
Definition bootloader_config.hpp:630
constexpr uint32_t SPI_FLASH
SPI flash configuration register.
Definition bootloader_config.hpp:606
constexpr uint32_t HALL_CONFIG
Hall encoder configuration register.
Definition bootloader_config.hpp:670
constexpr uint32_t CLOCK_CONFIG
Clock configuration register.
Definition bootloader_config.hpp:662
constexpr uint32_t BOOT_CONFIG
Boot configuration register.
Definition bootloader_config.hpp:598
constexpr uint32_t GPIO_PU
GPIO pull-up enable register.
Definition bootloader_config.hpp:638
constexpr uint32_t ABN2_CONFIG
ABN encoder 2 configuration register.
Definition bootloader_config.hpp:686
constexpr uint32_t GPIO_EXT
GPIO extended configuration register.
Definition bootloader_config.hpp:654
constexpr uint32_t I2C_CONFIG
I2C EEPROM configuration register.
Definition bootloader_config.hpp:614
constexpr uint32_t COMM_CONFIG
Communication interface selection register.
Definition bootloader_config.hpp:588
constexpr uint32_t SPI_ENC_CONFIG
SPI encoder configuration register.
Definition bootloader_config.hpp:710
constexpr uint32_t RS485_DELAY
RS485 TXEN delay configuration register.
Definition bootloader_config.hpp:580
constexpr uint32_t GPIO_PD
GPIO pull-down enable register.
Definition bootloader_config.hpp:646
constexpr uint32_t LDO_CONFIG
LDO configuration register address.
Definition bootloader_config.hpp:564
constexpr uint32_t GPIO_OUT
GPIO output level register.
Definition bootloader_config.hpp:622
BootMode
Boot mode selection for motor control system startup.
Definition bootloader_config.hpp:63
@ GPIO7
Use GPIO7 for UART RX (default)
@ GPIO6
Use GPIO6 for SPI0 SCK (default)
@ GPIO6
Use GPIO6 for UART TX (default)
@ GPIO8
Use GPIO8 for RS485 TX enable.
@ GPIO2
Use GPIO2 for RS485 TX enable.
@ SPI0
Physical SPI0 interface.
Definition bootloader_config.hpp:9
OtpBurnError
OTP burn operation error codes.
Definition bootloader_protocol.hpp:239
MemoryBank
Memory bank identifiers for bootloader operations.
Definition bootloader_protocol.hpp:86
@ SPI_FLASH
External SPI Flash.
@ I2C_EEPROM
External I2C EEPROM.
InfoQuery
GET_INFO query types for retrieving system information.
Definition bootloader_protocol.hpp:105
Definition tmc9660_bootloader.cpp:22
Definition tmc9660_bootloader.cpp:25
Bootloader command structure for SPI (40-bit / 5-byte protocol).
Definition bootloader_protocol.hpp:312
Bootloader command structure for UART (64-bit / 8-byte protocol).
Definition bootloader_protocol.hpp:335
Complete bootloader configuration structure.
Definition bootloader_config.hpp:998
Feature flags indicating available bootloader capabilities.
Definition bootloader_protocol.hpp:151
static BootloaderFeatures fromValue(uint32_t value) noexcept
Parse from 32-bit value.
Definition bootloader_protocol.hpp:159
Bootloader reply structure for SPI (40-bit / 5-byte protocol).
Definition bootloader_protocol.hpp:362
static BootloaderReplySPI fromBuffer(const std::array< uint8_t, 5 > &in) noexcept
Deserialize from 5-byte SPI buffer.
Definition bootloader_protocol.hpp:367
Bootloader reply structure for UART (64-bit / 8-byte protocol).
Definition bootloader_protocol.hpp:397
static BootloaderReplyUART fromBuffer(const std::array< uint8_t, 8 > &in) noexcept
Deserialize from 8-byte UART buffer.
Definition bootloader_protocol.hpp:404
Bootloader version information structure.
Definition bootloader_protocol.hpp:132
static BootloaderVersion fromValue(uint32_t value) noexcept
Parse from 32-bit value (major in upper 16 bits, minor in lower 16 bits)
Definition bootloader_protocol.hpp:137
Git version control information from bootloader firmware.
Definition bootloader_protocol.hpp:177
static GitInfo fromValue(uint32_t value) noexcept
Parse from 32-bit value.
Definition bootloader_protocol.hpp:182
OTP burn operation result information.
Definition bootloader_protocol.hpp:255
static OtpBurnResult createSuccess() noexcept
Create success result.
Definition bootloader_protocol.hpp:261
static OtpBurnResult createError(OtpBurnError code) noexcept
Create error result.
Definition bootloader_protocol.hpp:270
OTP load operation result information.
Definition bootloader_protocol.hpp:217
static OtpLoadResult fromValue(uint32_t value) noexcept
Parse from 32-bit value.
Definition bootloader_protocol.hpp:222
External memory partition version information.
Definition bootloader_protocol.hpp:197
static PartitionVersion fromValue(uint32_t value) noexcept
Parse from 32-bit value.
Definition bootloader_protocol.hpp:202
Main TMC9660 bootloader interface class.
Communication interfaces for TMC9660 Parameter Mode devices using TMCL protocol over SPI.
#define TMC9660_LOG_DEBUG(comm_obj, level, tag,...)
Compile-time debug logging control for TMC9660 library.
Definition tmc9660_comm_interface.hpp:101