Bootloader Initialization
The TMC9660 bootloader initialization is CRITICAL and must be completed successfully before any motor control operations. This guide covers the essential bootloader setup.
Why Bootloader Initialization is Required
The TMC9660 operates in two modes:
- Bootloader Mode: Configuration and firmware update
- Parameter Mode: Motor control operation
Motor control will NOT work until the bootloader is properly initialized and the device transitions to Parameter Mode.
Critical Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
tmc9660::BootloaderConfig cfg{};
// ⚠️ CRITICAL: Must set boot mode to Parameter
cfg.boot.boot_mode = tmc9660::bootcfg::BootMode::Parameter;
// Start motor control after configuration
cfg.boot.start_motor_control = true;
// SPI configuration
cfg.spiComm.boot_spi_iface = tmc9660::bootcfg::SPIInterface::IFACE0;
// UART configuration (if using UART)
cfg.uart.baud_rate = tmc9660::bootcfg::BaudRate::BR115200;
cfg.uart.tx_pin = tmc9660::bootcfg::UartTxPin::GPIO6;
cfg.uart.rx_pin = tmc9660::bootcfg::UartRxPin::GPIO7;
// Clock configuration
cfg.clock.use_external = tmc9660::bootcfg::ClockSource::Internal;
// Initialize bootloader
auto result = driver.bootloaderInit(&cfg);
if (result != tmc9660::TMC9660::BootloaderInitResult::Success) {
// Handle failure - motor control will not work!
return -1;
}
Command Ordering (CRITICAL)
When START_MOTOR_CONTROL = 1 is written, the bootloader EXITS IMMEDIATELY!
❌ WRONG: Writing START_MOTOR_CONTROL early
1
2
3
4
// WRONG! Bootloader exits after this write
cfg.boot.start_motor_control = true; // Written first
// ... other configs ...
// ❌ These configs fail because bootloader already exited!
✅ CORRECT: Writing START_MOTOR_CONTROL last
The bootloaderInit() method automatically writes START_MOTOR_CONTROL = 1 LAST, after all other configurations. This is the correct order.
Initialization Sequence
- Hardware Reset (if
performReset=true)- Assert RST pin for 100ms
- Release RST pin
- Wait 100ms for stabilization
- Mode Detection
- Check if device is in bootloader or parameter mode
- If in parameter mode and config provided, reinitialize
- Configuration
- Write all bootloader settings
- LAST: Write
START_MOTOR_CONTROL = 1(if enabled)
- Motor Control Startup (if
start_motor_control=true)- Consume SESSION_START
- Verify TMCL communication
OTP vs Runtime Configuration
OTP Configuration (Production)
When START_MOTOR_CONTROL = 1 is burned into OTP:
- ✅ Autonomous operation (no host needed)
- ✅ Fastest startup
- ❌ Cannot reconfigure without hardware reset
- ❌ Bootloader commands not accepted after power-up
Runtime Configuration (Development)
When START_MOTOR_CONTROL = 1 is written at runtime:
- ✅ Flexible configuration during development
- ✅ Can test different settings
- ⚠️ Requires host to send configuration
- ⚠️ Longer startup time
Common Issues
Bootloader Initialization Fails
- Check Hardware Reset
- Verify RST pin toggling
- Wait at least 100ms after reset
- Verify Communication
- Check SPI/UART wiring
- Verify CS pin (SPI) or TX/RX pins (UART)
- Confirm clock speed ≤ 4 MHz (SPI)
- Check Configuration
- Must set
boot_mode = Parameter - Verify all required settings are provided
- Must set
No Reply from Bootloader
- Check Pin Connections
- Verify TX/RX pins not swapped (UART)
- Verify MOSI/MISO not swapped (SPI)
- Verify Protocol
- Check CRC/checksum calculation
- Confirm frame format
- Check Timing
- Add delays between commands
- Respect SPI delayed reply timing
Best Practices
- Always Initialize Bootloader First
1 2
// This MUST be done before any motor control auto result = driver.bootloaderInit(&cfg);
- Use Complete Configuration
1 2 3
// Set all required fields cfg.boot.boot_mode = tmc9660::bootcfg::BootMode::Parameter; cfg.boot.start_motor_control = true;
- Handle Errors
1 2 3 4
if (result != tmc9660::TMC9660::BootloaderInitResult::Success) { // Motor control will not work without successful initialization return false; }
- Verify Communication After Init
1 2 3 4 5
// Test that TMCL communication works auto version = driver.bootloaderGetVersion(); if (!version.has_value()) { // Communication failed }
Bootloader Commands Quick Reference
Command Codes
| Code | Command | Description |
|---|---|---|
| 0x00 | GET_INFO | Get bootloader info |
| 0x08 | GET_BANK | Get current bank |
| 0x09 | SET_BANK | Select memory bank |
| 0x0A | GET_ADDRESS | Get current address |
| 0x0B | SET_ADDRESS | Set memory address |
| 0x0C | READ_32 | Read 32-bit data |
| 0x0D | READ_32_INC | Read 32-bit + increment |
| 0x0E | READ_16 | Read 16-bit data |
| 0x0F | READ_16_INC | Read 16-bit + increment |
| 0x10 | READ_8 | Read 8-bit data |
| 0x11 | READ_8_INC | Read 8-bit + increment |
| 0x12 | WRITE_32 | Write 32-bit value |
| 0x13 | WRITE_32_INC | Write 32-bit + increment |
| 0x14 | WRITE_16 | Write 16-bit value |
| 0x15 | WRITE_16_INC | Write 16-bit + increment |
| 0x16 | WRITE_8 | Write 8-bit value |
| 0x17 | WRITE_8_INC | Write 8-bit + increment |
| 0x1D | NO_OP | No operation (get reply) |
| 0x1E | OTP_LOAD | Load OTP page |
| 0x1F | OTP_BURN | Burn OTP page |
Status Codes
| Code | Name | Description |
|---|---|---|
| 0 | OK | Command executed successfully |
| 1 | CMD_NOT_FOUND | Invalid command number |
| 3 | INVALID_ADDR | Invalid memory address |
| 4 | INVALID_VALUE | Invalid request value |
| 14 | INVALID_BANK | Invalid memory bank |
| 15 | BUSY | Bootloader busy (SPI only) |
| 17 | MEM_UNCONFIGURED | External memory not configured |
| 18 | OTP_ERROR | OTP command failed |
| 19 | SESSION_START | First SPI datagram after power-on (SPI only) |
| 20 | CMD_NOT_AVAILABLE | Command not available |
| 21 | BOOTLOADER_RESUMED | Returned to bootloader from motor control (SPI only) |
Memory Banks
| Bank | Name | Description |
|---|---|---|
| 0 | RAM | Internal SRAM |
| 1 | OTP | One-time programmable memory |
| 2 | SPI Flash | External SPI flash memory |
| 3 | I2C EEPROM | External I2C EEPROM |
| 4 | Reserved | Reserved for future use |
| 5 | CONFIG | Configuration memory bank |
Next Steps
- Troubleshooting - Common issues and solutions
- Platform Integration - Communication interface setup
- Quick Start - Minimal working example