| Crates.io | tmc2209-uart |
| lib.rs | tmc2209-uart |
| version | 0.1.0 |
| created_at | 2026-01-20 09:36:35.365545+00 |
| updated_at | 2026-01-20 09:36:35.365545+00 |
| description | A no_std Rust driver for the TMC2209 stepper motor driver via UART |
| homepage | https://github.com/FrenchPOC/tmc2209-rs |
| repository | https://github.com/FrenchPOC/tmc2209-rs |
| max_upload_size | |
| id | 2056221 |
| size | 2,075,127 |
A no_std Rust driver for the TMC2209 stepper motor driver, communicating via single-wire UART.
embedded-io traits for maximum portabilityno_std compatible: Perfect for embedded systemsdefmt support: For embedded debuggingAdd to your Cargo.toml:
[dependencies]
tmc2209-uart = "0.1"
use tmc2209_uart::{Tmc2209, MicrostepResolution};
// Create driver with UART peripheral and slave address 0
let mut driver = Tmc2209::new(uart, 0);
// Check connection
if driver.is_connected() {
// Configure motor current (IRUN=16, IHOLD=8, delay=1)
driver.set_current(16, 8, 1)?;
// Set 16 microsteps with interpolation to 256
driver.set_microsteps(MicrostepResolution::M16)?;
// Enable StealthChop for silent operation
driver.enable_stealthchop()?;
// Move motor using UART velocity control
driver.set_velocity(5000)?;
// Check status
let status = driver.drv_status()?;
if status.ot() {
// Overtemperature shutdown!
}
}
Enable the async feature:
[dependencies]
tmc2209-uart = { version = "0.1", default-features = false, features = ["async"] }
use tmc2209_uart::Tmc2209;
let mut driver = Tmc2209::new(uart, 0);
// Async methods have _async suffix
driver.set_current_async(16, 8, 1).await?;
driver.set_velocity_async(5000).await?;
let status = driver.drv_status_async().await?;
| 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 |
The TMC2209 uses a single-wire UART interface:
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.
use tmc2209::{Tmc2209, Chopconf, IholdIrun, DrvStatus};
// Read a register
let chopconf: Chopconf = driver.read_register()?;
println!("MRES: {}", chopconf.mres());
println!("TOFF: {}", chopconf.toff());
// Modify and write back
let mut chopconf = driver.read_register::<Chopconf>()?;
chopconf.set_mres(4) // 16 microsteps
.set_intpol(true) // Enable interpolation
.set_toff(3); // Enable driver
driver.write_register(&chopconf)?;
// Create a new register value
let mut irun = IholdIrun::new();
irun.set_irun(20)
.set_ihold(10)
.set_iholddelay(6);
driver.write_register(&irun)?;
// Read by address
let value = driver.read_raw(0x6F)?; // DRV_STATUS
// Write by address
driver.write_raw(0x10, 0x00071010)?; // IHOLD_IRUN
| 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 |
// Enable/disable driver
driver.set_enabled(true)?;
// Set velocity (VACTUAL-based motion)
driver.set_velocity(1000)?; // Forward
driver.set_velocity(-1000)?; // Reverse
driver.stop()?; // Stop
// Current control
driver.set_current(run, hold, delay)?;
// Microstep resolution
driver.set_microsteps(MicrostepResolution::M16)?;
// Silent operation
driver.enable_stealthchop()?;
// High-torque operation
driver.enable_spreadcycle()?;
// CoolStep (adaptive current)
driver.enable_coolstep(4, 2)?; // semin=4, semax=2
// Sensorless homing
driver.configure_stall_detection(50)?;
let status = driver.drv_status()?;
// Temperature
if status.otpw() { /* Warning >120C */ }
if status.ot() { /* Shutdown >150C */ }
// Faults
if status.short_detected() { /* Short circuit */ }
if status.open_load_detected() { /* Open coil */ }
// Motion
if status.stst() { /* Motor stopped */ }
if status.stealth() { /* StealthChop active */ }
// Current
let actual_current = status.cs_actual(); // 0-31
use tmc2209::{calculate_current_settings, velocity_to_vactual, DEFAULT_RSENSE};
// Calculate CS and VSENSE for target current
let (cs, vsense) = calculate_current_settings(800, DEFAULT_RSENSE)?; // 800mA
// Convert velocity to VACTUAL
let vactual = velocity_to_vactual(
100.0, // 100 steps/second
256, // 256 microsteps
12_000_000 // 12MHz clock
);
// Each driver needs a unique address (0-3)
let mut driver0 = Tmc2209::new(&mut uart, 0);
let mut driver1 = Tmc2209::new(&mut uart, 1);
let mut driver2 = Tmc2209::new(&mut uart, 2);
let mut driver3 = Tmc2209::new(&mut uart, 3);
// 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
See the examples/ directory for platform-specific examples:
basic.rs - Basic motor controlstealthchop.rs - Silent operation setupsensorless_homing.rs - Stall detection for homingThe 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)
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.