Wednesday, December 20, 2023

Library for TMC2209 driver - AVR

This library is used to control one or more TMC2209 modules via UART using an AVR microcontroller. Apart from reading and writing the TMC2209 registers, this library can also be used to drive a stepper motor by using the stepperCon library in the background. The stepperCon library provides non blocking functions by using an interrupt, to drive multiple stepper motors with custom acceleration and speed, to keep track of motor positions, 3-axis motor coordination, as well as other useful functions. UART library is also provided that can use up to 2 USART peripherals at the same time: one for interfacing with the driver IC and one for debugging (assuming the microcontroller has two USART peripherals). 

The serial communication has some extra features for checkup such as:

  • reads the interface transmission counter (IFCNT) register after every write to ensure the IC received the correct data.
  • compares CRCs.
  • checks if correct number of bytes has been received.
  • if an error occurs it retries 2 times then sets a communication error flag that the user can check after each function is executed and take the appropriate action if an error occurs. 

Library for TMC2209 driver - AVR
The principle of operation is read-modify-write so there is no need to keep all driver IC registers in memory. The library only uses an 8 bytes union structure called replyDatagram where received and data to send is stored temporary.

If you wish to learn on how to wire the TMC2209 driver, you can find a tutorial here.


Library Structure

The library consists of the following files:

TMC2209.h and TMC2209.c files are the main library files that contain configurations for the driver ic and functions to communicate with the ic over UART. There are also functions like move() and setAcceleration() that are just a wrapper for functions included in the stepperCon library.

uart.h and uart.c: UART library to serial interface with the driver IC. By default it uses UART 1 but it can be changed to 0 if your microcontroller doesn't have two uarts.

utils.h and utils.c: included by uart headers. Contains functions and settings that might be used by other libraries such as converting an integer or float to a string for example.

micros.h and micros.c: keeps track of microseconds with 40 microseconds resolution. Included by stepperCon.h. Default timer used is Timer2. Here F_CPU is defined as 16MHz. Consider modifying this if your microcontroller uses different frequency.

stepperCon.h and stepperCon.c: included by TMC2209.h. A library used to control stepper motor drivers.

stepperConfig.h and stepperConfig.c: included by stepperCon.h. Contains pin definitions and custom functions used to control the stepper motor driver.

Optionally a pin interrupt can be used to trigger an interrupt when StallGuard pulls DIAG pin high. Using this pin interrupt library you can easily setup an interrupt for any microcontroller pin.

Default Settings

At the beginning of the TMC2209.h file there are default settings that consist of:

TMC_UART_BAUD_RATE: UART baud rate for serial communication with the TMC2209 chip. Default 115200.

TMC_USE_HARDWARE_ENABLE_PIN: enable pin can be tied to ground and the driver can be disabled by software. Set to 1 if EN pin is controlled by the microcontroller or 0 if not.

After this settings, follows default values for the TMC2209 registers. These default values are written to the registers by TMC_init() that is called by TMC_addDriver(). If the TMC2209 chip is reset, the registers will be reset to their factory default values so the TMC_init() function must be used to reconfigure the registers with user default values.

F_CPU: is defined as 16MHz inside micros.h. Consider modifying this if your microcontroller uses different frequency.F_CPU is best to be defined inside project properties in Microchip Studio or a Makefile if custom Makefiles are used. Please see for more details.

MAX_NR_OF_STEPPER_DRIVERS: defined by stepperConfig.h. This sets the maximum number of drivers supported, by setting the size of an array. Default is 2.


TMC2209 library usage example: in this file is some working code showing how most of the functions could be implemented.


Create a structure object for each driver

TMC* TMC_addDriver(uint8_t driver_addr, uint16_t steps_per_revolution)

Initialization function used to add a driver. Creates a stepperCon and a TMC structure objects. Calls TMC_init().

driver_addr: serial address of the driver

steps_per_revolution: steps per revolution (usually 200). For 1.8 degree motor (360 / 1.8 = 200).

Return: a pointer to a TMC structure object or null in case of an error such as there is no more room in the array for a new object pointer.


uint8_t TMC_init(TMC* self)

Used by TMC_addDriver(). Used to initiate the UART and to set the TMC2209 to default values using macros that can have values defined by the user.

self: pointer to a TMC structure object

Return: 1 if a serial communication error occurs

Read IC version

uint8_t TMC_readVersion(TMC* self)

Returns IC version. 0x21 = first version of the IC.

Read Global Status Flags (GSTAT)

uint8_t TMC_readStatus(TMC* self)

Returns the 3 status bits of the GSTAT register and clears them on the device. Use the associated functions to check which flag is set.

GSTAT check if module was Reset

uint8_t TMC_statusIsReset(TMC* self)

Returns 1 if the device has been reset since the last read. The initialization function should be used again to reconfigure the registers. Use after TMC_readStatus().

GSTAT check if Driver Error

uint8_t TMC_statusDriverError(TMC* self)

Returns 1 if the driver has been shut down due to over temperature or short circuit detection since the last read access. Read DRV_STATUS for details. The flag can only be cleared when all error conditions are cleared. Use after TMC_readStatus().

GSTAT check if Charge Pump is Under Voltage

uint8_t TMC_statusChargePumpUV(TMC* self)

Returns 1 if an under voltage on the charge pump has been detected. The driver is disabled in this case. This flag is not latched and thus does not need to be cleared. Use after TMC_readStatus().

Status check for UART error

uint8_t TMC_deviceConnectionLost(TMC* self)

Returns 1 if the communication failure flag is set. This flag is set when CRC fails, wrong number of bytes is received or write confirmation is not received. The flag is cleared automatically by read/write functions.

Read register PWM_SCALE_SUM

uint8_t TMC_readPwmScaleSum(TMC* self)

Information about the motor state is available with automatic scaling by reading out PWM_SCALE_SUM. As this parameter reflects the actual voltage required to drive the target current into the motor, it depends on several factors: motor load, coil resistance, supply voltage, and current setting. Therefore, an evaluation of the PWM_SCALE_SUM value allows checking the motor operation point. When reaching the limit (255), the current regulator cannot sustain the full motor current, e.g. due to a drop in supply volage.

Return: PWM_SCALE_SUM (0...255) which is bits 0...7 of the PWM_SCALE register

Read register PWM_SCALE_AUTO

int16_t TMC_readPwmScaleAuto(TMC* self)

Read back of the actual StealthChop voltage PWM scaling correction as determined by the current regulator. Should regulate to a value close to 0 during tuning procedure. Scaling value becomes frozen when operating in SpreadCycle.

Return: PWM_SCALE_AUTO (-255...255) which is bits 16...24 of the PWM_SCALE register

Read register PWM_OFS_AUTO

uint8_t TMC_readPwmOfsAuto(TMC* self)

Automatically determined offset value. Allow monitoring of the automatic tuning and determination of initial values for PWM_OFS and PWM_GRAD.

Return: PWM_OFS_AUTO (0...255) which is bits 0...7 of the PWM_AUTO register

Read register PWM_GRAD_AUTO

uint8_t TMC_readPwmGradAuto(TMC* self)

Automatically determined gradient value. Allow monitoring of the automatic tuning and determination of initial values for PWM_OFS and PWM_GRAD.

Return: PWM_GRAD_AUTO (0...255) which is bits 16...23 of the PWM_AUTO register

Read register SG_RESULT (StallGuard)

uint16_t TMC_readStallGuard(TMC* self)

SG_RESULT becomes updated with each fullstep. A higher value signals a lower motor load and more torque headroom. Intended for StealthChop mode, only.

Return: StallGuard result (0...510). A higher reading indicates less mechanical load. A lower reading indicates a higher load and thus a higher load angle.

Read register TSTEP

uint32_t TMC_readTstep(TMC* self)

Actual measured time between two 1/256 microsteps derived from the step input frequency in units of 1/fCLK. Measured value is (2^20)-1 in case of overflow or stand still.

Read register DRV_STATUS (Driver Status Flags)

uint32_t TMC_readDrvStat(TMC* self)

Return: the 32 bits register. Use the associated functions to check each driver status.

DRV_STATUS check Standstill

bool TMC_checkStandstil(TMC* self)

This flag indicates motor stand still in each operation mode. This occurs 2^20 clocks after the last step pulse.

Return: 0 or 1 flag

DRV_STATUS check Chopper type

bool TMC_checkChopper(TMC* self)

1: Driver operates in StealthChop mode

0: Driver operates in SpreadCycle mode

Return: 0 or 1 flag representing active chopper type. Optionally, the following macros are available that can be used in an if condition for more readability.

// Chopper mode returned by DRV_STATUS


uint8_t TMC_checkCS(TMC* self)

Actual current control scaling, for monitoring the function of the automatic current scaling.

Return: current scaling value (0...31)

DRV_STATUS check Temperature flags

uint8_t TMC_checkTempFlags(TMC* self)

Return: 4 flags, each flag representing a temperature threshold. Bit 0 (t120), bit 1 (t143), bit 2 (t150), bit 3 (t157). See datasheet chapter 15.1 for details.

DRV_STATUS check Open Load flags

uint8_t TMC_checkOpenLoad(TMC* self)

1: Open load detected on phase A or B. 

Hint: This is just an informative flag. The driver takes no action upon it. False detection may occur in fast motion and standstill. Check during slow motion, only.

Return: 2 flags. Bit 0 (open load indicator phase A), bit 1 (open load indicator phase B). See datasheet chapter 15.3 for details.

DRV_STATUS check Low Side Short flags

uint8_t TMC_checkLowSideShort(TMC* self)

1: Short on low-side MOSFET detected on phase A or B. 

The driver becomes disabled. The flags stay active, until the driver is disabled by software (TOFF=0) or by the ENN input. Flags are separate for both chopper modes.

Return: 2 flags. Bit 0 (low side short indicator phase A), bit 1 (low side short indicator phase B). See datasheet chapter 15.2 for details.

DRV_STATUS check Short to Ground flags

uint8_t TMC_checkShortToGnd(TMC* self)

1: Short to GND detected on phase A or B. 

The driver becomes disabled. The flags stay active, until the driver is disabled by software (TOFF=0) or by the ENN input. Flags are separate for both chopper modes.

Return: 2 flags. Bit 0 (short to ground indicator phase A), bit 1 (short to ground indicator phase B). See datasheet chapter 15.2 for details.

DRV_STATUS check Over-temperature flag

uint8_t TMC_checkOverTemp(TMC* self)

1: The selected over-temperature limit has been reached. 

Drivers become disabled until otpw (Over Temperature Pre-warning) is also cleared due to cooling down of the IC. The over-temperature flag is common for both bridges.

Return: 0 or 1 flag. See datasheet chapter 15.1 for details.

DRV_STATUS check Over-temperature Pre-warning flag

uint8_t TMC_checkOverTempPreWarning(TMC* self)

1: The selected over-temperature pre-warning threshold is exceeded. 

The over-temperature pre-warning flag is common for both bridges.

Return: 0 or 1 flag. See datasheet chapter 15.1 for details.

Set micro-step resolution

void TMC_setMicrostepResolution(TMC* self, uint16_t mstep_res)

Select microstep resolution. Sets MRES bits in CHOPCONF register. 

Example: the stepper is 200 steps/revolution type, and microstep resolution is 16 (mstep_res is MRES_16). To make a full revolution now the stepper requires 3200 steps (200 * 16).

mstep_res: microstep resolution as defined by these macros: MRES_1, MRES_2, MRES_4, MRES_8, MRES_16, MRES_32, MRES_64, MRES_128, MRES_256. The suffix represents the micro-step resolution.

Set speed

void TMC_setSpeed(TMC* self, float speed)

Sets the speed in steps per second at which the motor should run. The set speed can only be reached if there is enough steps (distance), if not, try increasing the acceleration. The maximum speed can be reached when the speed profile is trapezoid. Wrapper of stepperCon.stepperSetSpeed().

Set acceleration and deceleration

void TMC_setAcceleration(TMC* self, uint32_t acceleration)
void TMC_setDeceleration(TMC* self, uint32_t deceleration)

Set acceleration and deceleration rate in steps/second2. The given values will be multiplied by ACCEL_SCALING_FACTOR(100) so the user can use a lower number for readability. So instead of using 15000 use 150. The deceleration will also be set with the acceleration value, so in case that they have the same value, running setDeceleration() is not necessary. Wrapper of stepperCon.stepperSetAcceleration().

Set direction

void TMC_setDirection(TMC* self, int8_t dir)

Inverse spin direction. Sets the shaft bit in GCONF register. Normally the direction is set by the move() functions based on the sign of the distance but if that doesn't fit your needs then the direction can be inverted.

dir: direction can be one of the following macros: DIRECTION_CW or DIRECTION_CCW (clockwise or counter-clockwise).

Set chopper

void TMC_setChopper(TMC* self, bool chopper)

Switch between StealthChop and SpreadCycle. Sets en_SpreadCycle bit in GCONF register.

chopper: available macros:


Set SpreadCycle threshold

uint32_t TMC_spreadCycleThreshold(TMC* driver, uint16_t rpm_threshold)

Writes TPWMTHRS register. When the velocity exceeds the limit set by TPWMTHRS, the driver switches to SpreadCycle. Set TPWMTHRS zero if you want to work with StealthChop only. RPM is converted to TSTEP.

rpm_threshold: velocity threshold in RPM. When the motor velocity exceeds this threshold, the driver switches to SpreadCycle.

Return: calculated TSTEP value written to the register.

Set register IHOLD_IRUN

void TMC_setIrunIhold(TMC* driver, uint8_t irun, uint8_t ihold, uint8_t ihold_delay)

Used to set the motor run and standstill current.

irun: (reset default=31). Motor run current (0=1/32 … 31=32/32). Hint: Choose sense resistors in a way, that normal IRUN is 16 to 31 for best micro-step performance.

ihold: (reset default: OTP). Standstill current (0=1/32 … 31=32/32). In combination with StealthChop mode, setting IHOLD=0 allows to choose freewheeling or coil short circuit (passive braking) for motor stand still.

ihold_delay: (reset default: OTP). Controls the number of clock cycles for motor power down after standstill is detected (stst=1) and TPOWERDOWN has expired. The smooth transition avoids a motor jerk upon power down.

0: instant power down

1..15: delay per current reduction step in multiple of 2^18 clocks

Hint: use the provided spreadsheet calculator to convert time to number of clocks.

Set register TPOWERDOWN

void TMC_setTPowerDown(TMC* driver, uint8_t value)

Sets the delay time from stand still detection to motor current power down. Time range is about 0 to 5.6 seconds. 0…((2^8)-1) * 2^18 tCLK.

Attention: A minimum setting of 2 is required to allow automatic tuning of StealthChop PWM_OFFS_AUTO. 

value = time(s) / (1 / 12MHz) / 2^18

tclk = 1 / 12MHz

time(s) = value * 2^18 * tclk

value: 0 to 255 (255 is 5.6 seconds). Reset default = 20.

Hint: use the provided spreadsheet calculator to convert time to register value.

Set register SGTHRS (StallGuard)

void TMC_setStallGuard(TMC* self, uint8_t value)

Detection threshold for stall. The StallGuard value SG_RESULT becomes compared to the double of this threshold.

Note: Sensor-less homing/probing is not recommend for machines that use a lead screws due to high torque.

value: SGTHRS value (0...255). This value controls the StallGuard4 threshold level for stall detection. It compensates for motor specific characteristics and controls sensitivity. A higher value gives a higher sensitivity. A higher value makes StallGuard4 more sensitive and requires less torque to indicate a stall. The stall output becomes active if SG_RESULT fall below this value. See datasheet chapter 11 for details.

Set register TCOOLTHRS (CoolStep threshold)

void TMC_coolStepThreshold(TMC* self, uint16_t rpm_threshold)

This is the lower threshold velocity for switching on smart energy CoolStep and StallGuard to DIAG output. Set this parameter to disable CoolStep at low speeds, where it cannot work reliably. The stall output signal become enabled when exceeding this velocity. It becomes disabled again once the velocity falls below this threshold. Specifies lower CoolStep velocity by comparing the threshold value to TSTEP. RPM is converted to TSTEP inside the function.


- CoolStep is enabled, if configured (only with StealthChop) 

- Stall output signal on pin DIAG is enabled

rpm_threshold: velocity threshold in RPM. When the motor velocity exceeds this threshold, the driver enables CoolStep and stall output. See datasheet chapter 12 for details.

Set register COOLCONF (CoolStep)

void TMC_setCoolStep(TMC* self, uint16_t semin, uint8_t semax, uint8_t seup, uint8_t sedn, uint8_t seimin)

Register values for CoolStep configuration. See datasheet chapter 12 for details.

semin: minimum StallGuard value for smart current control and smart current enable (1...15). If the StallGuard result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle. 

0000: smart current control CoolStep off 

0001…1111: 1…15

semax: StallGuard hysteresis value for smart current control (1...15). If the StallGuard result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.

0000…1111: 0…15

seup: current up step width. Current increment steps per measured StallGuard value. 

00…11: 1, 2, 4, 8

Optionally these macros could be use for clarity:

SEUP_1		0
SEUP_2		1
SEUP_4		2
SEUP_8		3

sedn: current down step speed.

%00: For each 32 StallGuard4 values decrease by one 

%01: For each 8 StallGuard4 values decrease by one 

%10: For each 2 StallGuard4 values decrease by one 

%11: For each StallGuard4 value decrease by one

Optionally these macros could be use for clarity:

SEDN_32		0
SEDN_8		1
SEDN_2		2
SEDN_1		3

seimin: minimum current for smart current control.

0: 1/2 of current setting (IRUN)

1: 1/4 of current setting (IRUN)

Optionally these macros could be use for clarity:

SEIMIN_1_2	0 // 1/2 of current setting (IRUN)
SEIMIN_1_4	1 // 1/4 of current setting (IRUN)

Set Standstill mode

void TMC_setStandstillMode(TMC* self, uint8_t mode)

Sets freewheel bits in PWMCONF register. Only available with StealthChop enabled. The freewheeling option makes the motor easy movable, while both coil short options realize a passive brake. See datasheet chapter 6.7 for details.


- IHOLD must be set to 0 first except when using normal operation (mode 00).

- Open load flag will be set

- Driver must be enabled

mode: stand still option when motor current setting is zero (I_HOLD=0).

00: Normal operation 

01: Freewheeling 

10: Coil shorted using LS drivers 

11: Coil shorted using HS drivers

Available macros:

// Freewheel bits

Enable/disable driver by software

void TMC_softEnable(TMC* self, bool enable)

To save microcontroller pins, the enable pin can be connected to ground, thus keeping the drive enabled and then disable the driver using this function. Writes TOFF bits inside the CHOPCONF register.


0 - disable the driver by setting TOFF to 0

1 - restores TOFF off time with previous values

Move to absolute position

void TMC_moveTo(TMC* self, int32_t absolute)

Move the motor using absolute positioning. The stepping is not done here. The function sets the logical and pin direction and calculates the number of steps necessary to reach the target position then sets a flag that triggers the segment generation function that in turn triggers the ISR where the stepping is done. Negative values changes direction. Wrapper of stepperCon.stepperMoveTo().

This function will not do anything if:

- the motor is still running

- the current position and target position are the same

- the set speed is 0

Move to relative position

void TMC_move(TMC* self, int32_t relative)

Move the motor using relative positioning. See TMC_moveTo().

Start TMC2209 internal step generator

void TMC_startStepGeneratorRPS(TMC* self, float rps, uint16_t accel, float* accel_time_ms, float* vactual_p)

Writes VACTUAL register that allows moving the motor by UART control. Converts RPS speed to a "vactual" value which represents rotation speed in revolutions per second. A value other than 0 will activate the internal step generator. With internal oscillator:  VACTUAL = v[Hz] / 0.715Hz.

After setting the internal step generator, the motor will spin at set velocity even if the microcontroller is at sleep. However, the TMC2209 chip doesn't provide acceleration and deceleration. At slow speeds this might not be an issue but after a certain speed, the acceleration is needed. This function implements a simple but efficient acceleration algorithm using the micros library.

rps: velocity in RPS. If negative the rotation will be inverted. If 0 the motor will be controlled by the STEP input pin.

accel: acceleration value (e.g. 20, 100)

accel_time_ms: pointer to a float variable defined by the user where the total acceleration time will be stored. Needed for deceleration.

vactual_p: pointer to a float variable defined by the user where vactual value will be saved. Needed for deceleration.

void TMC_startStepGeneratorRPM(TMC* self, float rpm, uint16_t accel, float* accel_time_ms, float* vactual_p)

Wrapper to TMC_startStepGeneratorRPS() that converts RPM to RPS.

rpm: velocity in RPM. If negative the rotation will be inverted. If 0 the motor will be controlled by the STEP input pin.

Stop TMC2209 internal step generator

void TMC_stopStepGenerator(TMC* self, uint16_t decel, float accel_time_ms, float vactual)

Stops the internal step generator using deceleration.

decel: deceleration value (e.g. 20, 100)

accel_time_ms: saved value during acceleration

vactual_p: saved value during acceleration

StealthChop Automatic Tuning (AT)

void TMC_stealthChopAT(TMC* self, uint8_t irun)

Automatic Tuning (AT) of StealthChop following AT#1 step from the datasheet (chapter 6.1). This function could be used before homing or any other movement that must be done for AT#2.

The function does the following:

- Sets IHOLD same as IRUN

- Sets TPOWERDOWN to a larger value to prevent automatic power down before tuning is done

- Sets SpreadCycle threshold to 0 to prevent changing to SpreadCycle at threshold velocity

- Enables the driver using EN pin or by software

- Makes 1 step to exit a potential stand still mode

- Wait for 130ms

irun: IRUN current scale value used in your application (1...31)


v1.2 TMC2209 library Folder including all files that can be downloaded as a zip file
v1.0 TMC2209 calculator.ods LibreOffice spreadsheet ods file

TMC2209 datasheet v1.03.pdf TMC2209 datasheet that includes bookmarks
External Links More information about TMC drivers
v1.2 2/2/2024:
- added support for modern AVR devices (UPDI devices).
v1.0 TMC2209 release date 20, December, 2023, under GNU GPL v3 license

No comments:

Post a Comment