tmc2209-uart
A no_std Rust driver for the TMC2209 stepper motor driver, communicating via single-wire UART.
Features
- Full register support: All 24 TMC2209 registers with type-safe bitfield accessors
- Blocking and async APIs: Choose based on your platform needs
- Platform-agnostic: Uses
embedded-iotraits for maximum portability no_stdcompatible: Perfect for embedded systems- Multi-driver support: Daisy-chain up to 4 drivers (addresses 0-3)
- Utility functions: Current calculation, velocity conversion helpers
- Optional
defmtsupport: For embedded debugging
Quick Start
Add to your Cargo.toml:
[]
= "0.1"
Basic Usage (Blocking)
use ;
// Create driver with UART peripheral and slave address 0
let mut driver = new;
// Check connection
if driver.is_connected
Async Usage
Enable the async feature:
[]
= { = "0.1", = false, = ["async"] }
use Tmc2209;
let mut driver = new;
// Async methods have _async suffix
driver.set_current_async.await?;
driver.set_velocity_async.await?;
let status = driver.drv_status_async.await?;
Features
| Feature | Default | Description |
|---|---|---|
blocking |
Yes | Enable blocking API using embedded-io |
async |
No | Enable async API using embedded-io-async |
defmt |
No | Enable defmt::Format for debugging |
UART Configuration
The TMC2209 uses a single-wire UART interface:
- Baud rate: 115200 (default, configurable via OTP)
- Format: 8N1 (8 data bits, no parity, 1 stop bit)
- Mode: Half-duplex (single wire for TX and RX)
Hardware Connection
MCU TX ──┬── TMC2209 PDN_UART
│
MCU RX ──┘
Connect both TX and RX to PDN_UART through appropriate level shifting if needed. A 1K resistor in series with TX is recommended.
Register Access
Type-Safe Register Access
use ;
// Read a register
let chopconf: Chopconf = driver.read_register?;
println!;
println!;
// Modify and write back
let mut chopconf = driver.?;
chopconf.set_mres // 16 microsteps
.set_intpol // Enable interpolation
.set_toff; // Enable driver
driver.write_register?;
// Create a new register value
let mut irun = new;
irun.set_irun
.set_ihold
.set_iholddelay;
driver.write_register?;
Raw Register Access
// Read by address
let value = driver.read_raw?; // DRV_STATUS
// Write by address
driver.write_raw?; // IHOLD_IRUN
Available Registers
| Address | Name | Access | Description |
|---|---|---|---|
| 0x00 | GCONF | RW | Global configuration |
| 0x01 | GSTAT | R+WC | Global status flags |
| 0x02 | IFCNT | R | Interface transmission counter |
| 0x03 | SLAVECONF | W | UART slave configuration |
| 0x04 | OTP_PROG | W | OTP programming |
| 0x05 | OTP_READ | R | OTP memory read |
| 0x06 | IOIN | R | Input pin states |
| 0x07 | FACTORY_CONF | RW | Factory configuration |
| 0x10 | IHOLD_IRUN | W | Hold/run current settings |
| 0x11 | TPOWERDOWN | W | Power down delay |
| 0x12 | TSTEP | R | Measured step time |
| 0x13 | TPWMTHRS | W | StealthChop velocity threshold |
| 0x14 | TCOOLTHRS | W | CoolStep velocity threshold |
| 0x22 | VACTUAL | W | UART velocity control |
| 0x40 | SGTHRS | W | StallGuard threshold |
| 0x41 | SG_RESULT | R | StallGuard result |
| 0x42 | COOLCONF | W | CoolStep configuration |
| 0x6A | MSCNT | R | Microstep counter |
| 0x6B | MSCURACT | R | Microstep current |
| 0x6C | CHOPCONF | RW | Chopper configuration |
| 0x6F | DRV_STATUS | R | Driver status |
| 0x70 | PWMCONF | RW | StealthChop PWM configuration |
| 0x71 | PWM_SCALE | R | PWM scaling result |
| 0x72 | PWM_AUTO | R | Automatic PWM values |
Convenience Methods
Motor Control
// Enable/disable driver
driver.set_enabled?;
// Set velocity (VACTUAL-based motion)
driver.set_velocity?; // Forward
driver.set_velocity?; // Reverse
driver.stop?; // Stop
// Current control
driver.set_current?;
// Microstep resolution
driver.set_microsteps?;
Mode Selection
// Silent operation
driver.enable_stealthchop?;
// High-torque operation
driver.enable_spreadcycle?;
// CoolStep (adaptive current)
driver.enable_coolstep?; // semin=4, semax=2
// Sensorless homing
driver.configure_stall_detection?;
Status Monitoring
let status = driver.drv_status?;
// Temperature
if status.otpw
if status.ot
// Faults
if status.short_detected
if status.open_load_detected
// Motion
if status.stst
if status.stealth
// Current
let actual_current = status.cs_actual; // 0-31
Utility Functions
use ;
// Calculate CS and VSENSE for target current
let = calculate_current_settings?; // 800mA
// Convert velocity to VACTUAL
let vactual = velocity_to_vactual;
Multi-Driver Setup (Daisy Chain)
// Each driver needs a unique address (0-3)
let mut driver0 = new;
let mut driver1 = new;
let mut driver2 = new;
let mut driver3 = new;
// Configure addresses via MS1/MS2 pins on hardware:
// MS1=0, MS2=0 -> Address 0
// MS1=1, MS2=0 -> Address 1
// MS1=0, MS2=1 -> Address 2
// MS1=1, MS2=1 -> Address 3
Examples
See the examples/ directory for platform-specific examples:
basic.rs- Basic motor controlstealthchop.rs- Silent operation setupsensorless_homing.rs- Stall detection for homing
Protocol Details
The TMC2209 uses a simple datagram-based UART protocol:
Read Request (4 bytes):
┌──────┬────────────┬──────────┬─────┐
│ 0x05 │ Slave Addr │ Reg Addr │ CRC │
└──────┴────────────┴──────────┴─────┘
Write Request (8 bytes):
┌──────┬────────────┬──────────────┬───────────────┬─────┐
│ 0x05 │ Slave Addr │ Reg Addr|0x80│ Data (32-bit) │ CRC │
└──────┴────────────┴──────────────┴───────────────┴─────┘
Read Response (8 bytes):
┌──────┬──────┬──────────┬───────────────┬─────┐
│ 0x05 │ 0xFF │ Reg Addr │ Data (32-bit) │ CRC │
└──────┴──────┴──────────┴───────────────┴─────┘
CRC-8 polynomial: 0x07 (SAE J1850)
Related Resources
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.