├── .gitignore ├── LICENSE ├── README.md ├── examples ├── TMC5160_Config_Wizard │ └── TMC5160_Config_Wizard.ino ├── TMC5160_SPI │ └── TMC5160_SPI.ino └── TMC5160_UART │ └── TMC5160_UART.ino ├── library.json ├── library.properties └── src ├── Bitfield.h ├── TMC5160.cpp ├── TMC5160.h ├── TMC5160_SPI.cpp ├── TMC5160_UART.cpp └── TMC5160_registers.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | .pio 32 | platformio.ini 33 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tom Magnier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TMC5160 2 | Trinamic TMC5160 Arduino Library 3 | 4 | This library is intended as a basic driver library for controlling Trinamic TMC5160 stepper motor driver chips in SPI or UART mode. 5 | 6 | It is tested on ESP32, Teensy and SAMD (Zero) platforms. 7 | 8 | Originally based on Estee_TMC5130 code by Mike Estee. 9 | 10 | Check out more examples at https://github.com/tommag/Estee_TMC5130/tree/master/examples (the API should be almost compatible) -------------------------------------------------------------------------------- /examples/TMC5160_Config_Wizard/TMC5160_Config_Wizard.ino: -------------------------------------------------------------------------------- 1 | /* TMC5160 Config wizard 2 | 3 | This code gives an easy way to configure the various driver setting of a Trinamic TMC5160-based driver. 4 | 5 | Hardware setup : 6 | 7 | 8 | ///// Option 1 : UART 9 | A RS485 transceiver must be connected to the Serial1 pins with the TX Enable pin 10 | accessible to the uC 11 | 12 | Tie the A output to the TMC5160 I/O voltage with a 1k resistor. 13 | 14 | The TMC5160 Enable line must be connected to GND to enable the driver. 15 | 16 | 3.3/5V 17 | + +-----------------+ 18 | +-----+---+-+ VCC_IO | 19 | | | | | 20 | +++ | | | 21 | +-----------+ +--------------+ | | 1k +-+ SWSEL | 22 | | | | | +++ | | 23 | | RX 0 +--------+ RO | | | | 24 | | | | A +-------+-----------+ SWP NAI + ---| 25 | | | +---+ /RE | | | | 26 | | TX EN 2 +----+ | B +-------------------+ SWN | GND 27 | | | +---+ DE | | | 28 | | | | GND +----+ +----+ DRV_ENN | 29 | | TX 1 +--------+ DI | | | | | 30 | | | | | +---------+----+ GND | 31 | +-----------+ +--------------+ +-----------------+ 32 | Arduino / Teensy MAX3485 TMC5160 33 | or equivalent 34 | 35 | Tie CLK16 to GND to use the TMC5160 internal clock. 36 | Tie SPI_MODE to GND, SD_MODE to GND for UART mode. 37 | Connect NAI to GND (address 0). 38 | 39 | ///// Option 2 : SPI 40 | Connect the following pins to the TMC5160 : 41 | MOSI (Teensy : 11) <=> SDI 42 | MISO (Teensy : 12) <=> SDO 43 | SCK (Teensy : 13) <=> SCK 44 | 5 <=> CSN 45 | 8 <=> DRV_ENN (optional, tie to GND if not used) 46 | GND <=> GND 47 | 3.3V/5V <=> VCC_IO (depending on the processor voltage) 48 | 49 | Tie CLK16 to GND to use the TMC5160 internal clock. 50 | Tie SPI_MODE to VCC_IO, SD_MODE to GND for SPI mode. 51 | 52 | For both options, the TMC5160 VS pin must also be powered. 53 | 54 | Copyright (c) 2017 Tom Magnier 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a copy 57 | of this software and associated documentation files (the "Software"), to deal 58 | in the Software without restriction, including without limitation the rights 59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | copies of the Software, and to permit persons to whom the Software is 61 | furnished to do so, subject to the following conditions: 62 | 63 | The above copyright notice and this permission notice shall be included in all 64 | copies or substantial portions of the Software. 65 | 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 72 | SOFTWARE. 73 | */ 74 | 75 | #include 76 | #include 77 | 78 | const uint8_t UART_TX_EN = 2; // Differential transceiver TX enable pin 79 | const uint32_t UART_BAUDRATE = 500000; // UART baudrate : up to 750kbps with default TMC5160 clock 80 | const uint8_t SPI_CS = 5; // CS pin in SPI mode 81 | const uint8_t SPI_DRV_ENN = 8; // DRV_ENN pin in SPI mode 82 | 83 | TMC5160* motor; 84 | 85 | char readCommandToken(const char *tokens, int tokenCount, int defaultToken = -1) 86 | { 87 | char answer = 0; 88 | 89 | while (answer == 0) 90 | { 91 | //Flush input buffer 92 | while (Serial.available()) 93 | Serial.read(); 94 | 95 | for (int i = 0; i < tokenCount-1; i++) 96 | { 97 | Serial.print(tokens[i]); 98 | Serial.print('/'); 99 | } 100 | if (tokenCount > 0) 101 | Serial.print(tokens[tokenCount-1]); 102 | 103 | if (defaultToken >= 0 && defaultToken < tokenCount) 104 | { 105 | Serial.print(" (Default: "); 106 | Serial.print(tokens[defaultToken]); 107 | } 108 | Serial.println("):"); 109 | 110 | while (!Serial.available()); 111 | 112 | char readChar = Serial.read(); 113 | 114 | if (defaultToken >= 0 && defaultToken < tokenCount && isSpace(readChar)) 115 | { 116 | answer = tokens[defaultToken]; 117 | break; 118 | } 119 | 120 | for (int i = 0; i < tokenCount; i++) 121 | if (readChar == tokens[i]) 122 | answer = readChar; 123 | 124 | if (answer == 0) 125 | Serial.print("Invalid character. "); 126 | } 127 | 128 | return answer; 129 | } 130 | 131 | void printDriverStatus() 132 | { 133 | //TODO inside library 134 | 135 | TMC5160_Reg::GSTAT_Register gstat = {0}; 136 | gstat.value = motor->readRegister(TMC5160_Reg::GSTAT); 137 | 138 | if (gstat.uv_cp) 139 | { 140 | Serial.println("Charge pump undervoltage"); 141 | } 142 | else if (gstat.drv_err) 143 | { 144 | Serial.print("DRV_STATUS: "); 145 | TMC5160_Reg::DRV_STATUS_Register drvStatus = {0}; 146 | drvStatus.value = motor->readRegister(TMC5160_Reg::DRV_STATUS); 147 | Serial.print(drvStatus.value, HEX); 148 | Serial.print(" ("); 149 | if (drvStatus.s2vsa) 150 | Serial.print("Short to supply phase A"); 151 | else if (drvStatus.s2vsb) 152 | Serial.print("Short to supply phase B"); 153 | else if (drvStatus.s2ga) 154 | Serial.print("Short to ground phase A"); 155 | else if (drvStatus.s2gb) 156 | Serial.print("Short to ground phase B"); 157 | else if (drvStatus.ot) 158 | Serial.print("Overtemperature"); 159 | Serial.println(")"); 160 | } 161 | else 162 | { 163 | Serial.println("No error condition."); 164 | } 165 | } 166 | 167 | void tunePowerStage(TMC5160::PowerStageParameters *powerParams); 168 | void getPowerStageParams(TMC5160::PowerStageParameters *powerParams); 169 | void tuneMotorCurrent(TMC5160::MotorParameters *motorParams); 170 | void tuneStealthChop(TMC5160::PowerStageParameters *powerParams, TMC5160::MotorParameters *motorParams); 171 | void getStealthChopParams(TMC5160::MotorParameters *motorParams); 172 | void startPowerStageTroubleshooting(); 173 | 174 | void setup() 175 | { 176 | // init serial coms 177 | Serial.begin(115200); 178 | while(!Serial); 179 | 180 | Serial.print("Select the communication interface to use ('s' for SPI, 'u' for UART)"); 181 | if (readCommandToken("su", 2, 1) == 's') 182 | { 183 | SPI.begin(); 184 | motor = new TMC5160_SPI(SPI_CS); 185 | pinMode(SPI_DRV_ENN, OUTPUT); 186 | digitalWrite(SPI_DRV_ENN, LOW); // Active low 187 | 188 | // Check if the TMC5160 answers back 189 | TMC5160_Reg::IOIN_Register ioin = { 0 }; 190 | 191 | while (ioin.version != motor->IC_VERSION) 192 | { 193 | ioin.value = motor->readRegister(TMC5160_Reg::IO_INPUT_OUTPUT); 194 | 195 | if (ioin.value == 0 || ioin.value == 0xFFFFFFFF) 196 | { 197 | Serial.println("No TMC5160 found."); 198 | delay(2000); 199 | } 200 | else 201 | { 202 | Serial.println("Found a TMC device."); 203 | Serial.print("IC version: 0x"); 204 | Serial.print(ioin.version, HEX); 205 | Serial.print(" ("); 206 | if (ioin.version == motor->IC_VERSION) 207 | Serial.println("TMC5160)."); 208 | else 209 | Serial.println("unknown IC !)"); 210 | } 211 | } 212 | } 213 | else 214 | { 215 | // Init TMC serial bus @ 500kbps 216 | Serial1.begin(UART_BAUDRATE); 217 | Serial1.setTimeout(2); // TMC5160 should answer back immediately when reading a register. 218 | 219 | TMC5160_UART_Generic * _motor; // Temp pointer for UART specific functions 220 | //Use Serial1 (hardware UART on Feather M0/Teensy) ; address 0 (NAI LOW) 221 | _motor = new TMC5160_UART_Transceiver(UART_TX_EN, Serial1, 0, UART_BAUDRATE); 222 | _motor->setCommunicationMode(TMC5160_UART::RELIABLE_MODE); 223 | 224 | motor = _motor; 225 | 226 | // TMC5160 research on bus 227 | TMC5160_UART::ReadStatus readStatus; 228 | TMC5160_Reg::IOIN_Register ioin = { 0 }; 229 | 230 | while (ioin.version != _motor->IC_VERSION) 231 | { 232 | ioin.value = _motor->readRegister(TMC5160_Reg::IO_INPUT_OUTPUT, &readStatus); 233 | 234 | switch (readStatus) 235 | { 236 | case TMC5160_UART::SUCCESS: 237 | Serial.print("Found a TMC device at address "); 238 | Serial.println(_motor->getSlaveAddress()); 239 | Serial.print("IC version: 0x"); 240 | Serial.print(ioin.version, HEX); 241 | Serial.print(" ("); 242 | if (ioin.version == _motor->IC_VERSION) 243 | Serial.println("TMC5160)."); 244 | else 245 | Serial.println("unknown IC !)"); 246 | break; 247 | 248 | case TMC5160_UART::NO_REPLY: 249 | Serial.println("No TMC5160 found."); 250 | delay(2000); 251 | break; 252 | 253 | case TMC5160_UART::BAD_CRC: 254 | Serial.println("A TMC device replied with a bad CRC."); 255 | delay(100); 256 | break; 257 | } 258 | } 259 | } 260 | 261 | /* Driver status */ 262 | Serial.println(); 263 | Serial.print("Global status: 0x"); 264 | Serial.println(motor->readRegister(TMC5160_Reg::GSTAT), HEX); 265 | Serial.print("Driver status: 0x"); 266 | Serial.println(motor->readRegister(TMC5160_Reg::DRV_STATUS), HEX); 267 | 268 | /* Power stage tuning */ 269 | TMC5160::PowerStageParameters powerParams; 270 | Serial.println(); 271 | Serial.println("Start power stage tuning ? "); 272 | if (readCommandToken("yn", 2, 1) == 'y') 273 | tunePowerStage(&powerParams); 274 | else 275 | getPowerStageParams(&powerParams); 276 | 277 | /* Motor tuning */ 278 | TMC5160::MotorParameters motorParams; 279 | Serial.println(); 280 | tuneMotorCurrent(&motorParams); 281 | 282 | /* stealthChop tuning */ 283 | Serial.println(); 284 | Serial.println("Start stealthChop PWM mode tuning ? "); 285 | if (readCommandToken("yn", 2, 0) == 'y') 286 | tuneStealthChop(&powerParams, &motorParams); 287 | else 288 | getStealthChopParams(&motorParams); 289 | 290 | 291 | Serial.println("Driver and motor tuning is finished. To use the determined parameters you can"); 292 | Serial.println("copy and paste the following code snippet: "); 293 | Serial.println(); 294 | 295 | Serial.println("TMC5160::PowerStageParameters powerStageParams;"); 296 | Serial.println("TMC5160::MotorParameters motorParams;"); 297 | Serial.print("powerStageParams.drvStrength = "); 298 | Serial.print(powerParams.drvStrength); 299 | Serial.println(";"); 300 | Serial.print("powerStageParams.bbmTime = "); 301 | Serial.print(powerParams.bbmTime); 302 | Serial.println(";"); 303 | Serial.print("powerStageParams.bbmClks = "); 304 | Serial.print(powerParams.bbmClks); 305 | Serial.println(";"); 306 | Serial.print("motorParams.globalScaler = "); 307 | Serial.print(motorParams.globalScaler); 308 | Serial.println(";"); 309 | Serial.print("motorParams.irun = "); 310 | Serial.print(motorParams.irun); 311 | Serial.println(";"); 312 | Serial.print("motorParams.ihold = "); 313 | Serial.print(motorParams.ihold); 314 | Serial.println(";"); 315 | Serial.print("motorParams.freewheeling = "); 316 | Serial.print(motorParams.freewheeling); //TODO print human readable values 317 | Serial.println(";"); 318 | Serial.print("motorParams.pwmOfsInitial = "); 319 | Serial.print(motorParams.pwmOfsInitial); 320 | Serial.println(";"); 321 | Serial.print("motorParams.pwmGradInitial = "); 322 | Serial.print(motorParams.pwmGradInitial); 323 | Serial.println(";"); 324 | Serial.println("motor.begin(powerStageParams, motorParams, TMC5160::NORMAL_MOTOR_DIRECTION);"); 325 | 326 | 327 | Serial.println(); 328 | while (Serial.available()) 329 | Serial.read(); 330 | Serial.println("Press any key to begin operation with the specified parameters."); 331 | while (!Serial.available()); 332 | 333 | motor->begin(powerParams, motorParams, TMC5160::NORMAL_MOTOR_DIRECTION); 334 | 335 | // //TODO TEMP, include in begin/tuning 336 | // TMC5160_Reg::SHORT_CONF_Register shortConf = { 0 }; 337 | // shortConf.s2vs_level = 6; //Default value 338 | // shortConf.s2g_level = 8; //Increased value at high voltages 339 | // shortConf.shortfilter = 1; //Default value 340 | // motor->writeRegister(TMC5160_Reg::SHORT_CONF, shortConf.value); 341 | 342 | motor->setCurrentPosition(0); 343 | motor->setAcceleration(800); 344 | motor->setMaxSpeed(400); 345 | motor->setTargetPosition(4000); 346 | } 347 | 348 | void loop() 349 | { 350 | //TODO serial commands => velocity / position operation and feedback (driver state, position, etc) 351 | 352 | //Run 1000 steps every time a serial line is sent. 353 | if (Serial.available()) 354 | { 355 | while (Serial.available()) 356 | Serial.read(); 357 | 358 | motor->setTargetPosition(motor->getCurrentPosition() + 1000); 359 | } 360 | 361 | static unsigned long lastFeedbackTime = millis(); 362 | if (millis() - lastFeedbackTime > 500) 363 | { 364 | lastFeedbackTime = millis(); 365 | char buffer[11]; 366 | Serial.print("GSTAT: "); 367 | sprintf(buffer, "0x%08x", motor->readRegister(TMC5160_Reg::GSTAT)); 368 | Serial.print(buffer); 369 | Serial.print("\tDRV_STATUS: "); 370 | sprintf(buffer, "0x%08x", motor->readRegister(TMC5160_Reg::DRV_STATUS)); 371 | Serial.println(buffer); 372 | } 373 | } 374 | 375 | void tunePowerStage(TMC5160::PowerStageParameters *powerParams) 376 | { 377 | /*while (Serial.available()) 378 | Serial.read(); 379 | Serial.println("Please disconnect motor and press any key when ready."); 380 | while (!Serial.available()); */ 381 | 382 | TMC5160_Reg::GSTAT_Register gstat = {0}; 383 | gstat.value = motor->readRegister(TMC5160_Reg::GSTAT); 384 | if (!gstat.reset) 385 | { 386 | while (Serial.available()) 387 | Serial.read(); 388 | Serial.println("Warning: the driver is not in its reset state. You may want to power cycle it then press any key."); 389 | while (!Serial.available()); 390 | } 391 | 392 | Serial.println("Starting operation with lowest current setting possible."); 393 | 394 | //Set short detectors to highest sensitivity 395 | TMC5160_Reg::SHORT_CONF_Register shortConf = {0}; 396 | shortConf.s2vs_level = 4; 397 | shortConf.s2g_level = 2; 398 | shortConf.shortfilter = 0; 399 | motor->writeRegister(TMC5160_Reg::SHORT_CONF, shortConf.value); 400 | 401 | TMC5160::MotorParameters motorParams; 402 | motorParams.globalScaler = 32; 403 | motorParams.irun = 0; 404 | motorParams.ihold = 0; 405 | motor->begin(*powerParams, motorParams, TMC5160::NORMAL_MOTOR_DIRECTION); 406 | //motor->writeRegister(TMC5160_Reg::IO_INPUT_OUTPUT, 0); 407 | 408 | delay(200); 409 | 410 | //Check driver error state 411 | gstat.value = motor->readRegister(TMC5160_Reg::GSTAT); 412 | if (gstat.drv_err || gstat.uv_cp) 413 | { 414 | Serial.println(); 415 | Serial.println("/!\\ Driver error !"); 416 | printDriverStatus(); 417 | startPowerStageTroubleshooting(); 418 | } 419 | 420 | Serial.println(); 421 | Serial.println("You should monitor one of the bridge outputs (motor coil connections) using a scope."); 422 | Serial.println("Let's choose the MOSFET gate driver current. This should be the lowest setting giving slopes < 100ns"); 423 | 424 | TMC5160_Reg::DRV_CONF_Register drvConf = { 0 }; 425 | // Initial values 426 | drvConf.bbmclks = 4; 427 | drvConf.drvstrength = 2; 428 | motor->writeRegister(TMC5160_Reg::DRV_CONF, drvConf.value); 429 | 430 | char answer = 0; 431 | while (answer != 'd') 432 | { 433 | Serial.print("Current DRVSTRENGTH value: "); 434 | Serial.print(drvConf.drvstrength); 435 | Serial.print(". Press +/- to increase / decrease or d when done."); 436 | answer = readCommandToken("+-d", 3); 437 | 438 | switch (answer) 439 | { 440 | case '+': 441 | drvConf.drvstrength = constrain(drvConf.drvstrength+1, 0, 3); 442 | break; 443 | 444 | case '-': 445 | drvConf.drvstrength = constrain(drvConf.drvstrength-1, 0, 3); 446 | break; 447 | 448 | default: 449 | break; 450 | } 451 | motor->writeRegister(TMC5160_Reg::DRV_CONF, drvConf.value); 452 | } 453 | 454 | Serial.println(""); 455 | Serial.println("Let's tune the Break Before Make time. This should be as short as possible but still avoid overlapping of low side and high side MOSFETs."); 456 | Serial.println("If the switching slope is in the range 40-80ns, the smallest setting (0, 0) can be used."); 457 | Serial.println("You should check the bridge output for a small plateau at -1.2V or VM + 1.2V and tune its length"); 458 | Serial.println("Keep 30% of reserve !"); 459 | answer = 0; 460 | while (answer != 'd') 461 | { 462 | Serial.print("Current BBMCLKS: "); 463 | Serial.print(drvConf.bbmclks); 464 | Serial.print(". Press +/- to increase / decrease BBM raw value or d when done."); 465 | answer = readCommandToken("+-d", 3); 466 | 467 | switch (answer) 468 | { 469 | case '+': 470 | drvConf.bbmclks = constrain(drvConf.bbmclks+1, 0, 15); 471 | break; 472 | 473 | case '-': 474 | drvConf.bbmclks = constrain(drvConf.bbmclks-1, 0, 15); 475 | break; 476 | 477 | default: 478 | break; 479 | } 480 | motor->writeRegister(TMC5160_Reg::DRV_CONF, drvConf.value); 481 | } 482 | if (drvConf.bbmclks <= 4) 483 | { 484 | Serial.println(); 485 | Serial.println("BBMCLKS has a small value => BBMTIME can be used for fine tuning."); 486 | Serial.println("Keep 30% of reserve !"); 487 | drvConf.bbmtime = 24; 488 | drvConf.bbmclks = 0; 489 | motor->writeRegister(TMC5160_Reg::DRV_CONF, drvConf.value); 490 | 491 | answer = 0; 492 | while (answer != 'd') 493 | { 494 | Serial.print("Current BBMTIME: "); 495 | Serial.print(drvConf.bbmtime); 496 | Serial.print(". Press +/- to increase / decrease or d when done."); 497 | answer = readCommandToken("+-d", 3); 498 | 499 | switch (answer) 500 | { 501 | case '+': 502 | drvConf.bbmtime = constrain(drvConf.bbmtime+1, 0, 24); 503 | break; 504 | 505 | case '-': 506 | drvConf.bbmtime = constrain(drvConf.bbmtime-1, 0, 24); 507 | break; 508 | 509 | default: 510 | break; 511 | } 512 | motor->writeRegister(TMC5160_Reg::DRV_CONF, drvConf.value); 513 | } 514 | } 515 | 516 | Serial.println(); 517 | Serial.println("Power stage tuning is done."); 518 | Serial.print("Final DRV_CONF register value: 0x"); 519 | Serial.println(drvConf.value); 520 | Serial.print("DRVSTRENGTH: "); 521 | Serial.print(drvConf.drvstrength); 522 | Serial.print(", BBMTIME: "); 523 | Serial.print(drvConf.bbmtime); 524 | Serial.print(", BBMCLKS: "); 525 | Serial.println(drvConf.bbmclks); 526 | 527 | powerParams->drvStrength = drvConf.drvstrength; 528 | powerParams->bbmTime = drvConf.bbmtime; 529 | powerParams->bbmClks = drvConf.bbmclks; 530 | } 531 | 532 | void getPowerStageParams(TMC5160::PowerStageParameters *powerParams) 533 | { 534 | while (Serial.available()) 535 | Serial.read(); 536 | 537 | Serial.print("MOSFET gate driver current DRVSTRENGTH ? (Default: "); 538 | Serial.print(powerParams->drvStrength); 539 | Serial.println(")"); 540 | 541 | while (!Serial.available()); 542 | if (isDigit(Serial.peek())) 543 | powerParams->drvStrength = constrain(Serial.parseInt(), 0, 3); 544 | 545 | while (Serial.available()) 546 | Serial.read(); 547 | 548 | Serial.print("Break before make time BBMTIME ? (Default: "); 549 | Serial.print(powerParams->bbmTime); 550 | Serial.println(")"); 551 | 552 | while (!Serial.available()); 553 | if (isDigit(Serial.peek())) 554 | powerParams->bbmTime = constrain(Serial.parseInt(), 0, 24); 555 | 556 | while (Serial.available()) 557 | Serial.read(); 558 | 559 | Serial.print("Break before make clock cycles BBMCLKS ? (Default: "); 560 | Serial.print(powerParams->bbmClks); 561 | Serial.println(")"); 562 | 563 | while (!Serial.available()); 564 | if (isDigit(Serial.peek())) 565 | powerParams->bbmClks = constrain(Serial.parseInt(), 0, 15); 566 | 567 | Serial.print("Using DRVSTRENGTH: "); 568 | Serial.print(powerParams->drvStrength); 569 | Serial.print(", BBMTIME: "); 570 | Serial.print(powerParams->bbmTime); 571 | Serial.print(", BBMCLKS: "); 572 | Serial.println(powerParams->bbmClks); 573 | } 574 | 575 | void tuneMotorCurrent(TMC5160::MotorParameters *motorParams) 576 | { 577 | while (Serial.available()) 578 | Serial.read(); 579 | 580 | float senseResistor = 0.05; 581 | Serial.print("Sense resistors value ? (Default: "); 582 | Serial.print(senseResistor); 583 | Serial.println(" ohms)"); 584 | 585 | while (!Serial.available()); 586 | if (isDigit(Serial.peek())) 587 | senseResistor = Serial.parseFloat(); 588 | 589 | float maxRmsCurrent = 0.33 / (senseResistor * sqrt(2.0)); 590 | Serial.print(senseResistor); 591 | Serial.print("ohms => Max RMS current "); 592 | Serial.print(maxRmsCurrent); 593 | Serial.println("A"); 594 | 595 | while (Serial.available()) 596 | Serial.read(); 597 | 598 | float motorCurrent = 1.5; 599 | Serial.print("Motor phase current ? (Default: "); 600 | Serial.print(motorCurrent); 601 | Serial.println("A)"); 602 | 603 | while (!Serial.available()); 604 | if (isDigit(Serial.peek())) 605 | motorCurrent = Serial.parseFloat(); 606 | 607 | motorParams->globalScaler = constrain(floor(motorCurrent * 256.0 / maxRmsCurrent), 32, 256); 608 | Serial.print(motorCurrent); 609 | Serial.print("A => global scaler set to "); 610 | Serial.println(motorParams->globalScaler); 611 | 612 | motorParams->irun = constrain(floor(motorCurrent * 31.0 / (maxRmsCurrent * (float)motorParams->globalScaler / 256.0)), 0, 31); 613 | Serial.print("I_RUN set to "); 614 | Serial.println(motorParams->irun); 615 | 616 | while (Serial.available()) 617 | Serial.read(); 618 | 619 | float motorCurrentReduction = 0.5; 620 | Serial.print("Motor current standstill reduction ? Set to 0 for freewheeling / passive braking (Default: "); 621 | Serial.print(motorCurrentReduction); 622 | Serial.println(")"); 623 | 624 | while (!Serial.available()); 625 | if (isDigit(Serial.peek())) 626 | motorCurrentReduction = constrain(Serial.parseFloat(), 0.0, 1.0); 627 | 628 | motorParams->ihold = constrain(floor((float)motorParams->irun * motorCurrentReduction), 0, 31); 629 | Serial.print("I_HOLD set to "); 630 | Serial.println(motorParams->ihold); 631 | 632 | if (motorParams->ihold == 0) 633 | { 634 | Serial.println("Freewheeling options : 'n' for normal operation, 'f' for freewheeling, 'l' for passive braking using LS drivers"); 635 | switch (readCommandToken("nfl", 3, 0)) 636 | { 637 | case 'n': motorParams->freewheeling = TMC5160_Reg::FREEWHEEL_NORMAL; break; 638 | case 'f': motorParams->freewheeling = TMC5160_Reg::FREEWHEEL_ENABLED; break; 639 | case 'l': motorParams->freewheeling = TMC5160_Reg::FREEWHEEL_SHORT_LS; break; 640 | } 641 | } 642 | 643 | Serial.println("Motor current tuning is done."); 644 | } 645 | 646 | 647 | void tuneStealthChop(TMC5160::PowerStageParameters *powerParams, TMC5160::MotorParameters *motorParams) 648 | { 649 | Serial.println(); 650 | while (Serial.available()) 651 | Serial.read(); 652 | Serial.println("/!\\ WARNING : the determined parameters are only valid for this motor, at this operating voltage."); 653 | Serial.println("Please set the desired operating voltage now."); 654 | Serial.println("The tuning procedure will start a medium speed motion for at least 2 full turns."); 655 | Serial.println("Make sure that there is no mechanical issue then press any key."); 656 | while (!Serial.available()); 657 | 658 | //TODO TBL !! /!\ lower current limit, depends on motor and operating voltage 659 | //TODO PWM_REG ? 660 | 661 | Serial.println(); 662 | Serial.println("Starting operation with the determined current and default stealthChop values."); 663 | 664 | //Reset short detection if necessary 665 | TMC5160_Reg::SHORT_CONF_Register shortConf = {0}; 666 | shortConf.s2vs_level = 6; 667 | shortConf.s2g_level = 6; 668 | shortConf.shortfilter = 1; 669 | motor->writeRegister(TMC5160_Reg::SHORT_CONF, shortConf.value); 670 | 671 | motor->begin(*powerParams, *motorParams, TMC5160::NORMAL_MOTOR_DIRECTION); 672 | motor->writeRegister(TMC5160_Reg::TPOWERDOWN, 32); //For Phase #1, the motor needs to be at standstill, with the run current. 673 | 674 | Serial.println("Starting automatic tuning phase #1..."); 675 | motor->setAcceleration(200); 676 | motor->setRampMode(TMC5160::VELOCITY_MODE); 677 | motor->writeRegister(TMC5160_Reg::XACTUAL, 0); 678 | motor->setMaxSpeed(0.025); 679 | 680 | TMC5160_Reg::PWM_AUTO_Register pwmAuto = { 0 }; 681 | TMC5160_Reg::PWM_SCALE_Register pwmScale = { 0 }; 682 | 683 | do { 684 | pwmAuto.value = motor->readRegister(TMC5160_Reg::PWM_AUTO); 685 | pwmScale.value = motor->readRegister(TMC5160_Reg::PWM_SCALE); 686 | Serial.print("PWM_SCALE_AUTO: "); 687 | Serial.print(pwmScale.pwm_scale_auto); 688 | Serial.print(", PWM_OFS_AUTO: "); 689 | Serial.println(pwmAuto.pwm_ofs_auto); 690 | delay(100); 691 | } while ( !(abs((int32_t) motor->readRegister(TMC5160_Reg::XACTUAL)) > 10 && pwmScale.pwm_scale_auto == 0)); //Wait at least 10 steps. 692 | 693 | motor->stop(); 694 | pwmAuto.value = motor->readRegister(TMC5160_Reg::PWM_AUTO); 695 | pwmScale.value = motor->readRegister(TMC5160_Reg::PWM_SCALE); 696 | Serial.print("Phase #1 should be OK. PWM_OFS_AUTO: "); 697 | Serial.println(pwmAuto.pwm_ofs_auto); 698 | 699 | Serial.println(); 700 | Serial.println("Starting automatic tuning phase #2..."); 701 | motor->setAcceleration(400); 702 | 703 | float tuningMaxSpeed = 200.0;// 200 steps / sec => 60RPM on a 200 steps motor 704 | motor->setMaxSpeed(tuningMaxSpeed); 705 | 706 | TMC5160_Reg::RAMP_STAT_Register rampStat = { 0 }; 707 | 708 | do { 709 | pwmAuto.value = motor->readRegister(TMC5160_Reg::PWM_AUTO); 710 | pwmScale.value = motor->readRegister(TMC5160_Reg::PWM_SCALE); 711 | rampStat.value = motor->readRegister(TMC5160_Reg::RAMP_STAT); 712 | 713 | Serial.print("PWM_SCALE_AUTO: "); 714 | Serial.print(pwmScale.pwm_scale_auto); 715 | Serial.print(", PWM_SCALE_SUM: "); 716 | Serial.print(pwmScale.pwm_scale_sum); 717 | Serial.print(", PWM_GRAD_AUTO: "); 718 | Serial.println(pwmAuto.pwm_grad_auto); 719 | 720 | if (rampStat.velocity_reached) 721 | { 722 | if (pwmScale.pwm_scale_sum == 255 || pwmScale.pwm_scale_sum >= 4 * pwmAuto.pwm_ofs_auto) 723 | { 724 | tuningMaxSpeed /= 2.0; 725 | motor->setMaxSpeed(tuningMaxSpeed); 726 | Serial.println("Decreasing max speed."); 727 | } 728 | else if (pwmScale.pwm_scale_sum <= floor(1.5 * (float)pwmAuto.pwm_ofs_auto)) 729 | { 730 | tuningMaxSpeed *= 1.5; 731 | motor->setMaxSpeed(tuningMaxSpeed); 732 | Serial.println("Increasing max speed."); 733 | } 734 | } 735 | 736 | delay(100); 737 | } while ( !(pwmScale.pwm_scale_auto == 0 && rampStat.velocity_reached)); 738 | motor->stop(); 739 | 740 | pwmAuto.value = motor->readRegister(TMC5160_Reg::PWM_AUTO); 741 | Serial.print("Phase #2 is finished. PWM_GRAD_AUTO: "); 742 | Serial.println(pwmAuto.pwm_grad_auto); 743 | 744 | //TODO add timeout and try again the whole procedure if necessary. 745 | 746 | Serial.println(); 747 | Serial.println("/!\\ WARNING : You must run this tuning procedure again if the operating voltage changes."); 748 | //TODO it is advised to run at least the partial procedure during startup 749 | motorParams->pwmOfsInitial = pwmAuto.pwm_ofs_auto; 750 | motorParams->pwmGradInitial = pwmAuto.pwm_grad_auto; 751 | } 752 | 753 | 754 | void getStealthChopParams(TMC5160::MotorParameters *motorParams) 755 | { 756 | while (Serial.available()) 757 | Serial.read(); 758 | 759 | Serial.print("Initial PWM_OFS value ? (Default: "); 760 | Serial.print(motorParams->pwmOfsInitial); 761 | Serial.println(")"); 762 | 763 | while (!Serial.available()); 764 | if (isDigit(Serial.peek())) 765 | motorParams->pwmOfsInitial = constrain(Serial.parseInt(), 0, 255); 766 | 767 | while (Serial.available()) 768 | Serial.read(); 769 | 770 | Serial.print("Initial PWM_GRAD value ? (Default: "); 771 | Serial.print(motorParams->pwmGradInitial); 772 | Serial.println(")"); 773 | 774 | while (!Serial.available()); 775 | if (isDigit(Serial.peek())) 776 | motorParams->pwmGradInitial = constrain(Serial.parseInt(), 0, 255); 777 | 778 | 779 | Serial.print("Using PWM_OFS: "); 780 | Serial.print(motorParams->pwmOfsInitial); 781 | Serial.print(", PWM_GRAD: "); 782 | Serial.println(motorParams->pwmGradInitial); 783 | } 784 | 785 | 786 | void startPowerStageTroubleshooting() 787 | { 788 | Serial.println(); 789 | Serial.println("Do you want to troubleshoot the power stage ? This will disable then enable again while you look at the scope."); 790 | if (readCommandToken("yn", 2, 1)) 791 | { 792 | while (true) 793 | { 794 | while (Serial.available()) 795 | Serial.read(); 796 | Serial.println(); 797 | Serial.println("/!\\ Make sure that there is no real short condition and use a current limited power supply !"); 798 | Serial.println("Press any key to disable then enable again the driver."); 799 | while (!Serial.available()); 800 | 801 | //TODO add to library ! 802 | TMC5160_Reg::CHOPCONF_Register chopConf = {0}; 803 | chopConf.value = motor->readRegister(TMC5160_Reg::CHOPCONF); 804 | chopConf.toff = 0; //Disable driver 805 | motor->writeRegister(TMC5160_Reg::CHOPCONF, chopConf.value); 806 | 807 | delay(10); 808 | 809 | chopConf.toff = 5; //default 810 | motor->writeRegister(TMC5160_Reg::CHOPCONF, chopConf.value); 811 | 812 | delay(100); 813 | 814 | Serial.println("Driver status: "); 815 | printDriverStatus(); 816 | } 817 | } 818 | else 819 | { 820 | while (true); 821 | } 822 | } -------------------------------------------------------------------------------- /examples/TMC5160_SPI/TMC5160_SPI.ino: -------------------------------------------------------------------------------- 1 | /* TMC5160 SPI example 2 | 3 | This code demonstrates the usage of a Trinamic TMC5160 stepper driver in SPI mode. 4 | 5 | Hardware setup : 6 | Connect the following lines between the microcontroller board and the TMC5160 driver 7 | (Tested with a Teensy 3.2 and a TMC5160-BOB) 8 | 9 | MOSI (Teensy : 11) <=> SDI 10 | MISO (Teensy : 12) <=> SDO 11 | SCK (Teensy : 13) <=> SCK 12 | 5 <=> CSN 13 | 8 <=> DRV_ENN (optional, tie to GND if not used) 14 | GND <=> GND 15 | 3.3V/5V <=> VCC_IO (depending on the processor voltage) 16 | 17 | The TMC5160 VS pin must also be powered. 18 | Tie CLK16 to GND to use the TMC5160 internal clock. 19 | Tie SPI_MODE to VCC_IO, SD_MODE to GND. 20 | 21 | Please run the Config Wizard for power stage fine tuning / motor calibration (this code uses the default parameters and auto calibration). 22 | 23 | Copyright (c) 2020 Tom Magnier 24 | 25 | Permission is hereby granted, free of charge, to any person obtaining a copy 26 | of this software and associated documentation files (the "Software"), to deal 27 | in the Software without restriction, including without limitation the rights 28 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 29 | copies of the Software, and to permit persons to whom the Software is 30 | furnished to do so, subject to the following conditions: 31 | 32 | The above copyright notice and this permission notice shall be included in all 33 | copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 38 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 40 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 41 | SOFTWARE. 42 | */ 43 | 44 | #include 45 | #include 46 | 47 | const uint8_t SPI_CS = 5; // CS pin in SPI mode 48 | const uint8_t SPI_DRV_ENN = 8; // DRV_ENN pin in SPI mode 49 | 50 | TMC5160_SPI motor = TMC5160_SPI(SPI_CS); //Use default SPI peripheral and SPI settings. 51 | 52 | 53 | void setup() 54 | { 55 | // USB/debug serial coms 56 | Serial.begin(115200); 57 | 58 | pinMode(SPI_DRV_ENN, OUTPUT); 59 | digitalWrite(SPI_DRV_ENN, LOW); // Active low 60 | 61 | // This sets the motor & driver parameters /!\ run the configWizard for your driver and motor for fine tuning ! 62 | TMC5160::PowerStageParameters powerStageParams; // defaults. 63 | TMC5160::MotorParameters motorParams; 64 | motorParams.globalScaler = 98; // Adapt to your driver and motor (check TMC5160 datasheet - "Selecting sense resistors") 65 | motorParams.irun = 31; 66 | motorParams.ihold = 16; 67 | 68 | SPI.begin(); 69 | motor.begin(powerStageParams, motorParams, TMC5160::NORMAL_MOTOR_DIRECTION); 70 | 71 | // ramp definition 72 | motor.setRampMode(TMC5160::POSITIONING_MODE); 73 | motor.setMaxSpeed(400); 74 | motor.setAcceleration(500); 75 | 76 | Serial.println("starting up"); 77 | 78 | delay(1000); // Standstill for automatic tuning 79 | } 80 | 81 | void loop() 82 | { 83 | uint32_t now = millis(); 84 | static unsigned long t_dirchange, t_echo; 85 | static bool dir; 86 | 87 | // every n seconds or so... 88 | if ( now - t_dirchange > 3000 ) 89 | { 90 | t_dirchange = now; 91 | 92 | // reverse direction 93 | dir = !dir; 94 | motor.setTargetPosition(dir ? 200 : 0); // 1 full rotation = 200s/rev 95 | } 96 | 97 | // print out current position 98 | if( now - t_echo > 100 ) 99 | { 100 | t_echo = now; 101 | 102 | // get the current target position 103 | float xactual = motor.getCurrentPosition(); 104 | float vactual = motor.getCurrentSpeed(); 105 | Serial.print("current position : "); 106 | Serial.print(xactual); 107 | Serial.print("\tcurrent speed : "); 108 | Serial.println(vactual); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /examples/TMC5160_UART/TMC5160_UART.ino: -------------------------------------------------------------------------------- 1 | /* TMC5160 UART example 2 | 3 | This code demonstrates the usage of a Trinamic TMC5160 stepper driver using its 4 | single-wire interface. 5 | 6 | A RS485 transceiver must be connected to the Serial1 pins with the TX Enable pin 7 | accessible to the uC 8 | 9 | Tie the A output to the TMC5160 I/O voltage with a 1k resistor. 10 | 11 | The TMC5160 Enable line must be connected to GND to enable the driver. 12 | 13 | 3.3/5V 14 | + +-----------------+ 15 | +-----+---+-+ VCC_IO | 16 | | | | | 17 | +++ | | | 18 | +-----------+ +--------------+ | | 1k +-+ SWSEL | 19 | | | | | +++ | | 20 | | RX 0 +--------+ RO | | | | 21 | | | | A +-------+-----------+ SWP NAI + ---| 22 | | | +---+ /RE | | | | 23 | | TX EN 2 +----+ | B +-------------------+ SWN | GND 24 | | | +---+ DE | | | 25 | | | | GND +----+ +----+ DRV_ENN | 26 | | TX 1 +--------+ DI | | | | | 27 | | | | | +---------+----+ GND | 28 | +-----------+ +--------------+ +-----------------+ 29 | Arduino / Teensy MAX3485 TMC5160 30 | or equivalent 31 | 32 | Tie CLK16 to GND to use the TMC5160 internal clock. 33 | Tie SPI_MODE to GND, SD_MODE to GND for UART mode. 34 | Connect NAI to GND (address 0). 35 | 36 | Copyright (c) 2018-2021 Tom Magnier 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | */ 56 | 57 | #include 58 | #include 59 | 60 | const uint8_t UART_TX_EN_PIN = 5; // Differential transceiver TX enable pin 61 | const uint32_t UART_BAUDRATE = 500000; // UART baudrate : up to 750kbps with default TMC5160 clock 62 | 63 | TMC5160_UART_Transceiver motor = TMC5160_UART_Transceiver(UART_TX_EN_PIN, Serial1, 0, UART_BAUDRATE); //Use Serial 1 ; address 0 (NAI LOW) 64 | 65 | 66 | void setup() 67 | { 68 | // USB/debug serial coms 69 | Serial.begin(115200); 70 | 71 | // Init TMC serial bus @ 250kbps 72 | Serial1.begin(250000); 73 | Serial1.setTimeout(5); // TMC5130 should answer back immediately when reading a register. 74 | 75 | // status LED 76 | pinMode(LED_BUILTIN, OUTPUT); 77 | 78 | // This sets the motor & driver parameters /!\ run the configWizard for your driver and motor for fine tuning ! 79 | TMC5160::PowerStageParameters powerStageParams; // defaults. 80 | TMC5160::MotorParameters motorParams; 81 | motorParams.globalScaler = 98; // Adapt to your driver and motor (check TMC5160 datasheet - "Selecting sense resistors") 82 | motorParams.irun = 31; 83 | motorParams.ihold = 16; 84 | 85 | motor.begin(powerStageParams, motorParams, TMC5160::NORMAL_MOTOR_DIRECTION); 86 | 87 | // ramp definition 88 | motor.setRampMode(TMC5160::POSITIONING_MODE); 89 | motor.setMaxSpeed(400); 90 | motor.setAcceleration(500); 91 | 92 | Serial.println("starting up"); 93 | } 94 | 95 | void loop() 96 | { 97 | uint32_t now = millis(); 98 | static unsigned long t_dirchange, t_echo; 99 | static bool dir; 100 | 101 | // every n seconds or so... 102 | if ( now - t_dirchange > 3000 ) 103 | { 104 | t_dirchange = now; 105 | 106 | // reverse direction 107 | dir = !dir; 108 | motor.setTargetPosition(dir ? 200 : 0); // 1 full rotation = 200s/rev 109 | } 110 | 111 | // print out current position 112 | if( now - t_echo > 100 ) 113 | { 114 | t_echo = now; 115 | 116 | // get the current target position 117 | float xactual = motor.getCurrentPosition(); 118 | float vactual = motor.getCurrentSpeed(); 119 | Serial.print("current position : "); 120 | Serial.print(xactual); 121 | Serial.print("\tcurrent speed : "); 122 | Serial.println(vactual); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TMC5160", 3 | "keywords": "tmc5160, stepper, motor, trinamic, motion controller", 4 | "description": "Control Trinamic TMC5160 stepper motor driver with internal motion controller via SPI or UART", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/tommag/TMC5160_Arduino" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Tom Magnier", 14 | "maintainer": true 15 | } 16 | ], 17 | "dependencies": 18 | { 19 | "name": "ArxTypeTraits", 20 | "frameworks": "arduino" 21 | }, 22 | "version": "1.1.0", 23 | "frameworks": "arduino", 24 | "platforms": "*" 25 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TMC5160 2 | version=1.1.0 3 | author=Tom Magnier 4 | maintainer=Tom Magnier 5 | sentence=Trinamic TMC5160 library 6 | paragraph=Control TMC5160 stepper motor driver with internal motion controller via SPI or UART. 7 | category=Device Control 8 | url=https://github.com/tommag/TMC5160_Arduino 9 | architectures=* 10 | depends=ArxTypeTraits -------------------------------------------------------------------------------- /src/Bitfield.h: -------------------------------------------------------------------------------- 1 | /* From https://blog.codef00.com/2014/12/06/portable-bitfields-using-c11/ 2 | * No license defined. 3 | */ 4 | 5 | #ifndef BITFIELD_H_ 6 | #define BITFIELD_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // using std::uint8_t; 13 | // using std::uint16_t; 14 | // using std::uint32_t; 15 | // using std::uint64_t; 16 | 17 | // This library has been reported to produce undefined behavior. 18 | // Disabling GCC optimizations seem to fix the problem. 19 | #ifndef ESP_PLATFORM 20 | #pragma GCC push_options 21 | #pragma GCC optimize ("O0") 22 | #endif 23 | 24 | namespace { 25 | 26 | template 27 | struct MinimumTypeHelper { 28 | typedef 29 | typename std::conditional::type>::type>::type>::type>::type type; 35 | }; 36 | 37 | } 38 | 39 | template 40 | class BitField { 41 | private: 42 | enum { 43 | Mask = (1u << Bits) - 1u 44 | }; 45 | 46 | typedef typename MinimumTypeHelper::type T; 47 | public: 48 | template 49 | BitField &operator=(T2 value) { 50 | value_ = (value_ & ~((T)Mask << Index)) | (((T)value & (T)Mask) << Index); 51 | return *this; 52 | } 53 | 54 | operator T() const { return (value_ >> Index) & (T)Mask; } 55 | explicit operator bool() const { return value_ & ((T)Mask << Index); } 56 | BitField &operator++() { return *this = *this + 1; } 57 | T operator++(int) { T r = *this; ++*this; return r; } 58 | BitField &operator--() { return *this = *this - 1; } 59 | T operator--(int) { T r = *this; --*this; return r; } 60 | size_t size() const { return Bits; } 61 | 62 | private: 63 | T value_; 64 | }; 65 | 66 | 67 | template 68 | class BitField { 69 | private: 70 | enum { 71 | Bits = 1, 72 | Mask = 0x01 73 | }; 74 | 75 | typedef typename MinimumTypeHelper::type T; 76 | public: 77 | BitField &operator=(bool value) { 78 | value_ = (value_ & ~((T)Mask << Index)) | ((T)value << Index); 79 | return *this; 80 | } 81 | 82 | explicit operator bool() const { return value_ & ((T)Mask << Index); } 83 | 84 | private: 85 | T value_; 86 | }; 87 | 88 | #ifndef ESP_PLATFORM 89 | #pragma GCC pop_options 90 | #endif 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /src/TMC5160.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Mike Estee 5 | Copyright (c) 2017 Tom Magnier 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | #include "TMC5160.h" 27 | 28 | TMC5160::TMC5160(uint32_t fclk) 29 | : _fclk(fclk) 30 | { 31 | 32 | } 33 | 34 | TMC5160::~TMC5160() 35 | { 36 | ; 37 | } 38 | 39 | 40 | bool TMC5160::begin(const PowerStageParameters &powerParams, const MotorParameters &motorParams, MotorDirection stepperDirection ) 41 | { 42 | /* Clear the reset and charge pump undervoltage flags */ 43 | TMC5160_Reg::GSTAT_Register gstat = { 0 }; 44 | gstat.reset = true; 45 | gstat.uv_cp = true; 46 | writeRegister(TMC5160_Reg::GSTAT, gstat.value); 47 | 48 | TMC5160_Reg::DRV_CONF_Register drvConf = { 0 }; 49 | drvConf.drvstrength = constrain(powerParams.drvStrength, 0, 3); 50 | drvConf.bbmtime = constrain(powerParams.bbmTime, 0, 24); 51 | drvConf.bbmclks = constrain(powerParams.bbmClks, 0, 15); 52 | writeRegister(TMC5160_Reg::DRV_CONF, drvConf.value); 53 | 54 | writeRegister(TMC5160_Reg::GLOBAL_SCALER, constrain(motorParams.globalScaler, 32, 256)); 55 | 56 | // set initial currents and delay 57 | TMC5160_Reg::IHOLD_IRUN_Register iholdrun = { 0 }; 58 | iholdrun.ihold = constrain(motorParams.ihold, 0, 31); 59 | iholdrun.irun = constrain(motorParams.irun, 0, 31); 60 | iholdrun.iholddelay = 7; 61 | writeRegister(TMC5160_Reg::IHOLD_IRUN, iholdrun.value); 62 | 63 | // TODO set short detection / overcurrent protection levels 64 | 65 | // Set initial PWM values 66 | TMC5160_Reg::PWMCONF_Register pwmconf = { 0 }; 67 | pwmconf.value = 0xC40C001E; //Reset default 68 | pwmconf.pwm_autoscale = false; //Temp to set OFS and GRAD initial values 69 | if (_fclk > DEFAULT_F_CLK) 70 | pwmconf.pwm_freq = 0; 71 | else 72 | pwmconf.pwm_freq = 0b01; // recommended : 35kHz with internal typ. 12MHZ clock. 0b01 => 2/683 * f_clk 73 | pwmconf.pwm_grad = motorParams.pwmGradInitial; 74 | pwmconf.pwm_ofs = motorParams.pwmOfsInitial; 75 | pwmconf.freewheel = motorParams.freewheeling; 76 | writeRegister(TMC5160_Reg::PWMCONF, pwmconf.value); 77 | 78 | pwmconf.pwm_autoscale = true; 79 | pwmconf.pwm_autograd = true; 80 | writeRegister(TMC5160_Reg::PWMCONF, pwmconf.value); 81 | 82 | // Recommended settings in quick config guide 83 | _chopConf.toff = 5; 84 | _chopConf.tbl = 2; 85 | _chopConf.hstrt_tfd = 4; 86 | _chopConf.hend_offset = 0; 87 | writeRegister(TMC5160_Reg::CHOPCONF, _chopConf.value); 88 | 89 | // use position mode 90 | setRampMode(POSITIONING_MODE); 91 | 92 | TMC5160_Reg::GCONF_Register gconf = { 0 }; 93 | gconf.en_pwm_mode = true; //Enable stealthChop PWM mode 94 | gconf.shaft = stepperDirection; 95 | writeRegister(TMC5160_Reg::GCONF, gconf.value); 96 | 97 | //Set default start, stop, threshold speeds. 98 | setRampSpeeds(0, 0.1, 0); //Start, stop, threshold speeds 99 | 100 | //Set default D1 (must not be = 0 in positioning mode even with V1=0) 101 | writeRegister(TMC5160_Reg::D_1, 100); 102 | 103 | return false; 104 | } 105 | 106 | void TMC5160::end() 107 | { 108 | // no-op, just stop talking.... 109 | ; // FIXME: try and shutdown motor/chips? 110 | } 111 | 112 | bool TMC5160::isLastReadSuccessful() 113 | { 114 | return _lastRegisterReadSuccess; 115 | } 116 | 117 | void TMC5160::setRampMode(TMC5160::RampMode mode) 118 | { 119 | switch (mode) 120 | { 121 | case POSITIONING_MODE: 122 | writeRegister(TMC5160_Reg::RAMPMODE, TMC5160_Reg::POSITIONING_MODE); 123 | break; 124 | 125 | case VELOCITY_MODE: 126 | setMaxSpeed(0); // There is no way to know if we should move in the positive or negative direction => set speed to 0. 127 | writeRegister(TMC5160_Reg::RAMPMODE, TMC5160_Reg::VELOCITY_MODE_POS); 128 | break; 129 | 130 | case HOLD_MODE: 131 | writeRegister(TMC5160_Reg::RAMPMODE, TMC5160_Reg::HOLD_MODE); 132 | break; 133 | } 134 | 135 | _currentRampMode = mode; 136 | } 137 | 138 | float TMC5160::getCurrentPosition() 139 | { 140 | int32_t uStepPos = readRegister(TMC5160_Reg::XACTUAL); 141 | 142 | if ((uint32_t)(uStepPos) == 0xFFFFFFFF) 143 | return NAN; 144 | else 145 | return (float)uStepPos / (float)_uStepCount; 146 | } 147 | 148 | float TMC5160::getEncoderPosition() 149 | { 150 | int32_t uStepPos = readRegister(TMC5160_Reg::X_ENC); 151 | 152 | if ((uint32_t)(uStepPos) == 0xFFFFFFFF) 153 | return NAN; 154 | else 155 | return (float)uStepPos / (float)_uStepCount; 156 | } 157 | 158 | float TMC5160::getLatchedPosition() 159 | { 160 | int32_t uStepPos = readRegister(TMC5160_Reg::XLATCH); 161 | 162 | if ((uint32_t)(uStepPos) == 0xFFFFFFFF) 163 | return NAN; 164 | else 165 | return (float)uStepPos / (float)_uStepCount; 166 | } 167 | 168 | float TMC5160::getLatchedEncoderPosition() 169 | { 170 | int32_t uStepPos = readRegister(TMC5160_Reg::ENC_LATCH); 171 | 172 | if ((uint32_t)(uStepPos) == 0xFFFFFFFF) 173 | return NAN; 174 | else 175 | return (float)uStepPos / (float)_uStepCount; 176 | } 177 | 178 | float TMC5160::getTargetPosition() 179 | { 180 | int32_t uStepPos = readRegister(TMC5160_Reg::XTARGET); 181 | 182 | if ((uint32_t)(uStepPos) == 0xFFFFFFFF) 183 | return NAN; 184 | else 185 | return (float)uStepPos / (float)_uStepCount; 186 | } 187 | 188 | float TMC5160::getCurrentSpeed() 189 | { 190 | uint32_t data = readRegister(TMC5160_Reg::VACTUAL); 191 | 192 | if (data == 0xFFFFFFFF) 193 | return NAN; 194 | 195 | // Returned data is 24-bits, signed => convert to 32-bits, signed. 196 | if (bitRead(data, 23)) // highest bit set => negative value 197 | data |= 0xFF000000; 198 | 199 | return speedToHz(data); 200 | } 201 | 202 | void TMC5160::setCurrentPosition(float position, bool updateEncoderPos) 203 | { 204 | writeRegister(TMC5160_Reg::XACTUAL, (int32_t)(position * (float)_uStepCount)); 205 | 206 | if (updateEncoderPos) 207 | { 208 | writeRegister(TMC5160_Reg::X_ENC, (int32_t)(position * (float)_uStepCount)); 209 | clearEncoderDeviationFlag(); 210 | } 211 | } 212 | 213 | void TMC5160::setTargetPosition(float position) 214 | { 215 | writeRegister(TMC5160_Reg::XTARGET, (int32_t)(position * (float)_uStepCount)); 216 | } 217 | 218 | void TMC5160::setMaxSpeed(float speed) 219 | { 220 | writeRegister(TMC5160_Reg::VMAX, min(0x7FFFFF, speedFromHz(fabs(speed)))); // VMAX : 23 bits 221 | 222 | if (_currentRampMode == VELOCITY_MODE) 223 | { 224 | writeRegister(TMC5160_Reg::RAMPMODE, speed < 0.0f ? TMC5160_Reg::VELOCITY_MODE_NEG : TMC5160_Reg::VELOCITY_MODE_POS); 225 | } 226 | } 227 | 228 | void TMC5160::setRampSpeeds(float startSpeed, float stopSpeed, float transitionSpeed) 229 | { 230 | writeRegister(TMC5160_Reg::VSTART, min(0x3FFFF, speedFromHz(fabs(startSpeed)))); // VSTART : 18 bits 231 | writeRegister(TMC5160_Reg::VSTOP, min(0x3FFFF, speedFromHz(fabs(stopSpeed)))); // VSTOP : 18 bits 232 | writeRegister(TMC5160_Reg::V_1, min(0xFFFFF, speedFromHz(fabs(transitionSpeed)))); // V1 : 20 bits 233 | } 234 | 235 | void TMC5160::setAcceleration(float maxAccel) 236 | { 237 | writeRegister(TMC5160_Reg::AMAX, min(0xFFFF, accelFromHz(fabs(maxAccel)))); // AMAX, DMAX: 16 bits 238 | writeRegister(TMC5160_Reg::DMAX, min(0xFFFF, accelFromHz(fabs(maxAccel)))); 239 | } 240 | 241 | void TMC5160::setAccelerations(float maxAccel, float maxDecel, float startAccel, float finalDecel) 242 | { 243 | writeRegister(TMC5160_Reg::AMAX, min(0xFFFF, accelFromHz(fabs(maxAccel)))); // AMAX, DMAX, A1, D1 : 16 bits 244 | writeRegister(TMC5160_Reg::DMAX, min(0xFFFF, accelFromHz(fabs(maxDecel)))); 245 | writeRegister(TMC5160_Reg::A_1, min(0xFFFF, accelFromHz(fabs(startAccel)))); 246 | writeRegister(TMC5160_Reg::D_1, min(0xFFFF, accelFromHz(fabs(finalDecel)))); 247 | } 248 | 249 | /** 250 | * 251 | * @see Datasheet rev 1.15, section 6.3.2.2 "RAMP_STAT - Ramp & Reference Switch Status Register". 252 | * @return true if the target position has been reached, false otherwise. 253 | */ 254 | bool TMC5160::isTargetPositionReached(void) 255 | { 256 | TMC5160_Reg::RAMP_STAT_Register rampStatus = {0}; 257 | rampStatus.value = readRegister(TMC5160_Reg::RAMP_STAT); 258 | return rampStatus.position_reached ? true : false; 259 | } 260 | 261 | /** 262 | * 263 | * @see Datasheet rev 1.15, section 6.3.2.2 "RAMP_STAT - Ramp & Reference Switch Status Register". 264 | * @return true if the target velocity has been reached, false otherwise. 265 | */ 266 | bool TMC5160::isTargetVelocityReached(void) 267 | { 268 | TMC5160_Reg::RAMP_STAT_Register rampStatus = {0}; 269 | rampStatus.value = readRegister(TMC5160_Reg::RAMP_STAT); 270 | return rampStatus.velocity_reached ? true : false; 271 | } 272 | 273 | void TMC5160::stop() 274 | { 275 | // §14.2.4 Early Ramp Termination option b) 276 | writeRegister(TMC5160_Reg::VSTART, 0); 277 | writeRegister(TMC5160_Reg::VMAX, 0); 278 | } 279 | 280 | void TMC5160::disable() 281 | { 282 | TMC5160_Reg::CHOPCONF_Register chopconf = { 0 }; 283 | chopconf.value = _chopConf.value; 284 | chopconf.toff = 0; 285 | writeRegister(TMC5160_Reg::CHOPCONF, chopconf.value); 286 | } 287 | 288 | void TMC5160::enable() 289 | { 290 | writeRegister(TMC5160_Reg::CHOPCONF, _chopConf.value); 291 | } 292 | 293 | TMC5160::DriverStatus TMC5160::getDriverStatus() 294 | { 295 | TMC5160_Reg::GSTAT_Register gstat = {0}; 296 | gstat.value = readRegister(TMC5160_Reg::GSTAT); 297 | TMC5160_Reg::DRV_STATUS_Register drvStatus = {0}; 298 | drvStatus.value = readRegister(TMC5160_Reg::DRV_STATUS); 299 | 300 | if (gstat.uv_cp) 301 | return CP_UV; 302 | if (drvStatus.s2vsa) 303 | return S2VSA; 304 | if (drvStatus.s2vsb) 305 | return S2VSB; 306 | if (drvStatus.s2ga) 307 | return S2GA; 308 | if (drvStatus.s2gb) 309 | return S2GB; 310 | if (drvStatus.ot) 311 | return OT; 312 | if (gstat.drv_err) 313 | return OTHER_ERR; 314 | if (drvStatus.otpw) 315 | return OTPW; 316 | 317 | return OK; 318 | } 319 | 320 | const char* TMC5160::getDriverStatusDescription(DriverStatus st) 321 | { 322 | switch (st) 323 | { 324 | case OK: return "OK"; 325 | case CP_UV: return "Charge pump undervoltage"; 326 | case S2VSA: return "Short to supply phase A"; 327 | case S2VSB: return "Short to supply phase B"; 328 | case S2GA: return "Short to ground phase A"; 329 | case S2GB: return "Short to ground phase B"; 330 | case OT: return "Overtemperature"; 331 | case OTHER_ERR: return "Other driver error"; 332 | case OTPW: return "Overtemperature warning"; 333 | default: break; 334 | } 335 | 336 | return "Unknown"; 337 | } 338 | 339 | void TMC5160::setModeChangeSpeeds(float pwmThrs, float coolThrs, float highThrs) 340 | { 341 | writeRegister(TMC5160_Reg::TPWMTHRS, min(0xFFFFF, thrsSpeedToTstep(pwmThrs))); // 20 bits 342 | writeRegister(TMC5160_Reg::TCOOLTHRS, min(0xFFFFF, thrsSpeedToTstep(coolThrs))); 343 | writeRegister(TMC5160_Reg::THIGH, min(0xFFFFF, thrsSpeedToTstep(highThrs))); 344 | } 345 | 346 | bool TMC5160::setEncoderResolution(int32_t motorSteps, int32_t encResolution, bool inverted) 347 | { 348 | //See §22.2 349 | float factor = (float)motorSteps * (float)_uStepCount / (float)encResolution; 350 | 351 | //Check if the binary prescaler gives an exact match 352 | if ((int32_t)(factor * 65536.0f) * encResolution == motorSteps * _uStepCount * 65536) 353 | { 354 | TMC5160_Reg::ENCMODE_Register encmode = { 0 }; 355 | encmode.value = readRegister(TMC5160_Reg::ENCMODE); 356 | encmode.enc_sel_decimal = false; 357 | writeRegister(TMC5160_Reg::ENCMODE, encmode.value); 358 | 359 | int32_t encConst = (int32_t)(factor * 65536.0f); 360 | if (inverted) 361 | encConst = -encConst; 362 | writeRegister(TMC5160_Reg::ENC_CONST, encConst); 363 | 364 | #if 0 365 | Serial.println("Using binary mode"); 366 | Serial.print("Factor : 0x"); 367 | Serial.print(encConst, HEX); 368 | Serial.print(" <=> "); 369 | Serial.println((float)(encConst) / 65536.0f); 370 | #endif 371 | 372 | return true; 373 | } 374 | else 375 | { 376 | TMC5160_Reg::ENCMODE_Register encmode = { 0 }; 377 | encmode.value = readRegister(TMC5160_Reg::ENCMODE); 378 | encmode.enc_sel_decimal = true; 379 | writeRegister(TMC5160_Reg::ENCMODE, encmode.value); 380 | 381 | int integerPart = floor(factor); 382 | int decimalPart = (int)((factor - (float)integerPart) * 10000.0f); 383 | if (inverted) 384 | { 385 | integerPart = 65535 - integerPart; 386 | decimalPart = 10000 - decimalPart; 387 | } 388 | int32_t encConst = integerPart * 65536 + decimalPart; 389 | writeRegister(TMC5160_Reg::ENC_CONST, encConst); 390 | 391 | #if 0 392 | Serial.println("Using decimal mode"); 393 | Serial.print("Factor : 0x"); 394 | Serial.print(encConst, HEX); 395 | Serial.print(" <=> "); 396 | Serial.print(integerPart); 397 | Serial.print("."); 398 | Serial.println(decimalPart); 399 | #endif 400 | 401 | //Check if the decimal prescaler gives an exact match. Floats have about 7 digits of precision so no worries here. 402 | return ((int32_t)(factor * 10000.0f) * encResolution == motorSteps * (int32_t)_uStepCount * 10000); 403 | } 404 | } 405 | 406 | void TMC5160::setEncoderIndexConfiguration(TMC5160_Reg::ENCMODE_sensitivity_Values sensitivity, bool nActiveHigh, bool ignorePol, bool aActiveHigh, bool bActiveHigh) 407 | { 408 | TMC5160_Reg::ENCMODE_Register encmode = { 0 }; 409 | encmode.value = readRegister(TMC5160_Reg::ENCMODE); 410 | 411 | encmode.sensitivity = sensitivity; 412 | encmode.pol_N = nActiveHigh; 413 | encmode.ignore_AB = ignorePol; 414 | encmode.pol_A = aActiveHigh; 415 | encmode.pol_B = bActiveHigh; 416 | 417 | writeRegister(TMC5160_Reg::ENCMODE, encmode.value); 418 | } 419 | 420 | void TMC5160::setEncoderLatching(bool enabled) 421 | { 422 | TMC5160_Reg::ENCMODE_Register encmode = { 0 }; 423 | encmode.value = readRegister(TMC5160_Reg::ENCMODE); 424 | 425 | encmode.latch_x_act = true; 426 | encmode.clr_cont = enabled; 427 | 428 | writeRegister(TMC5160_Reg::ENCMODE, encmode.value); 429 | } 430 | 431 | void TMC5160::setEncoderAllowedDeviation(int steps) 432 | { 433 | writeRegister(TMC5160_Reg::ENC_DEVIATION, min(0xFFFFF, steps * _uStepCount)); // 20 bits 434 | } 435 | 436 | bool TMC5160::isEncoderDeviationDetected() 437 | { 438 | TMC5160_Reg::ENC_STATUS_Register encStatus = {0}; 439 | encStatus.value = readRegister(TMC5160_Reg::ENC_STATUS); 440 | return isLastReadSuccessful() && encStatus.deviation_warn; 441 | } 442 | 443 | void TMC5160::clearEncoderDeviationFlag() 444 | { 445 | TMC5160_Reg::ENC_STATUS_Register encStatus = {0}; 446 | encStatus.deviation_warn = true; 447 | writeRegister(TMC5160_Reg::ENC_STATUS, encStatus.value); 448 | } 449 | 450 | void TMC5160::setShortProtectionLevels(int s2vsLevel, int s2gLevel, int shortFilter, int shortDelay) 451 | { 452 | TMC5160_Reg::SHORT_CONF_Register shortConf = {0}; 453 | shortConf.s2vs_level = constrain(s2vsLevel, 4, 15); 454 | shortConf.s2g_level = constrain(s2gLevel, 2, 15); 455 | shortConf.shortfilter = constrain(shortFilter, 0, 3); 456 | shortConf.shortdelay = constrain(shortDelay, 0, 1); 457 | 458 | writeRegister(TMC5160_Reg::SHORT_CONF, shortConf.value); 459 | } 460 | -------------------------------------------------------------------------------- /src/TMC5160.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Mike Estee (Estee_TMC5160) 5 | Copyright (c) 2017 Tom Magnier 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | #ifndef TMC5160_H 27 | #define TMC5160_H 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | class TMC5160 { 34 | public: 35 | static constexpr uint8_t IC_VERSION = 0x30; 36 | static constexpr uint32_t DEFAULT_F_CLK = 12000000; // Typical internal clock frequency in Hz. 37 | 38 | enum MotorDirection { NORMAL_MOTOR_DIRECTION = 0x00, INVERSE_MOTOR_DIRECTION = 0x1 }; 39 | enum RampMode { POSITIONING_MODE, VELOCITY_MODE, HOLD_MODE }; 40 | 41 | enum DriverStatus { 42 | OK, // No error condition 43 | CP_UV, // Charge pump undervoltage 44 | S2VSA, // Short to supply phase A 45 | S2VSB, // Short to supply phase B 46 | S2GA, // Short to ground phase A 47 | S2GB, // Short to ground phase B 48 | OT, // Overtemperature (error) 49 | OTHER_ERR, // GSTAT drv_err is set but none of the above conditions is found. 50 | OTPW // Overtemperature pre warning 51 | }; 52 | 53 | struct PowerStageParameters { 54 | uint8_t drvStrength = 2; // MOSFET gate driver current (0 to 3) 55 | uint8_t bbmTime = 0; // "Break Before Make" duration specified in ns (0 to 24) 56 | uint8_t bbmClks = 4; // "Break Before Make" duration specified in clock cycles (0 to 15). 57 | }; 58 | 59 | struct MotorParameters { 60 | uint16_t globalScaler = 32; // global current scaling (32 to 256) 61 | uint8_t irun = 16; // motor run current (0 to 31). For best performance don't set lower than 16 62 | uint8_t ihold = 0; // standstill current (0 to 31). Set 70% of irun or lower. 63 | TMC5160_Reg::PWMCONF_freewheel_Values freewheeling = TMC5160_Reg::FREEWHEEL_NORMAL; // Freewheeling / passive braking of ihold = 0 64 | uint8_t pwmOfsInitial = 30; // initial stealthChop PWM amplitude offset (0-255) 65 | uint8_t pwmGradInitial = 0; // initial stealthChop velocity dependent gradient for PWM amplitude 66 | }; 67 | 68 | TMC5160(uint32_t fclk = DEFAULT_F_CLK); 69 | ~TMC5160(); 70 | 71 | /* Start the motor driver using the specified parameters. 72 | * These should be tuned according to the power stage and motor used. 73 | * Look in the examples for a config wizard. 74 | * powerParams : power stage parameters 75 | * motorParams : motor current parameters 76 | * stepperDirection : normal / inverted 77 | */ 78 | 79 | virtual bool begin(const PowerStageParameters &powerParams, const MotorParameters &motorParams, MotorDirection stepperDirection/*=NORMAL_MOTOR_DIRECTION*/); 80 | void end(); 81 | 82 | //TODO stealthChop tuning procedure 83 | 84 | virtual uint32_t readRegister(uint8_t address) = 0; // addresses are from TMC5160.h 85 | virtual uint8_t writeRegister(uint8_t address, uint32_t data) = 0; 86 | 87 | /* Check if the last register read was successful. This should be checked whenever 88 | a register read is used to take a decision. 89 | Reasons for failure can be : data bus disconnected, transmission error (bad CRC), etc 90 | This is mostly useful in UART mode. 91 | */ 92 | bool isLastReadSuccessful(); 93 | 94 | /* Ramp mode selection : 95 | - Positioning mode : autonomous move to XTARGET using all A, D and V parameters. 96 | - Velocity mode : follows VMAX and AMAX. Call setMaxSpeed() AFTER switching to velocity mode. 97 | - Hold mode : Keep current velocity until a stop event occurs. 98 | */ 99 | void setRampMode(RampMode mode); 100 | 101 | float getCurrentPosition(); // Return the current internal position (steps) 102 | float getEncoderPosition(); // Return the current position according to the encoder counter (steps) 103 | float getLatchedPosition(); // Return the position that was latched on the last ref switch / encoder event (steps) 104 | float getLatchedEncoderPosition(); // Return the encoder position that was latched on the last encoder event (steps) 105 | float getTargetPosition(); // Get the target position (steps) 106 | float getCurrentSpeed(); // Return the current speed (steps / second) 107 | 108 | 109 | void setCurrentPosition(float position, bool updateEncoderPos = false); // Set the current internal position (steps) and optionally update the encoder counter as well to keep them in sync. 110 | void setTargetPosition(float position); // Set the target position /!\ Set all other motion profile parameters before 111 | void setMaxSpeed(float speed); // Set the max speed VMAX (steps/second) 112 | void setRampSpeeds(float startSpeed, float stopSpeed, float transitionSpeed); // Set the ramp start speed VSTART, ramp stop speed VSTOP, acceleration transition speed V1 (steps / second). /!\ Set VSTOP >= VSTART, VSTOP >= 0.1 113 | void setAcceleration(float maxAccel); // Set the ramp acceleration / deceleration (steps / second^2) 114 | void setAccelerations(float maxAccel, float maxDecel, float startAccel, float finalDecel); // Set the ramp accelerations AMAX, DMAX, A1, D1 (steps / second^2) /!\ Do not set finalDecel to 0 even if transitionSpeed = 0 115 | 116 | bool isTargetPositionReached(void); // Return true if the target position has been reached 117 | bool isTargetVelocityReached(void); // Return true if the target velocity has been reached 118 | 119 | void stop(); // Stop the current motion according to the set ramp mode and motion parameters. The max speed and start speed are set to 0 but the target position stays unchanged. 120 | 121 | void disable(); //Disable the driver, all bridges off 122 | void enable(); //Enable the driver 123 | 124 | //TODO chopper config functions ? 125 | 126 | DriverStatus getDriverStatus(); // Get the current driver status (OK / error conditions) 127 | static const char* getDriverStatusDescription(DriverStatus st); // Get a human readable description of the given driver status 128 | 129 | /* Set the speeds (in steps/second) at which the internal functions and modes will be turned on or off. 130 | * Below pwmThrs, "stealthChop" PWM mode is used. 131 | * Between pwmThrs and highThrs, "spreadCycle" classic mode is used. 132 | * Between coolThrs and highThrs, "spreadCycle" is used ; "coolStep" current reduction and "stallGuard" load measurement can be enabled. 133 | * Above highThrs, "constant Toff" mode and fullstep mode can be enabled. 134 | * See the TMC 5160 datasheet for details and optimization. 135 | * Setting a speed to 0 will disable this threshold. 136 | */ 137 | void setModeChangeSpeeds(float pwmThrs, float coolThrs, float highThrs); 138 | 139 | /* Set the encoder constant to match the motor and encoder resolutions. 140 | * This function will determine if the binary or decimal mode should be used 141 | * and return false if no exact match could be found (for example for an encoder 142 | * with a resolution of 360 and a motor with 200 steps per turn). In this case 143 | * the best approximation in decimal mode will be used. 144 | * 145 | * Params : 146 | * motorSteps : the number of steps per turn for the motor 147 | * encResolution : the actual encoder resolution (pulses per turn) 148 | * inverted : whether the encoder and motor rotations are inverted 149 | * 150 | * Return : 151 | * true if an exact match was found, false otherwise 152 | */ 153 | bool setEncoderResolution(int32_t motorSteps, int32_t encResolution, bool inverted = false); 154 | 155 | /* Configure the encoder N event context. 156 | * Params : 157 | * sensitivity : set to one of ENCODER_N_NO_EDGE, ENCODER_N_RISING_EDGE, ENCODER_N_FALLING_EDGE, ENCODER_N_BOTH_EDGES 158 | * nActiveHigh : choose N signal polarity (true for active high) 159 | * ignorePol : if true, ignore A and B polarities to validate a N event 160 | * aActiveHigh : choose A signal polarity (true for active high) to validate a N event 161 | * bActiveHigh : choose B signal polarity (true for active high) to validate a N event 162 | */ 163 | void setEncoderIndexConfiguration(TMC5160_Reg::ENCMODE_sensitivity_Values sensitivity, bool nActiveHigh = true, bool ignorePol = true, bool aActiveHigh = false, bool bActiveHigh = false); 164 | 165 | /* Enable/disable encoder and position latching on each encoder N event (on each revolution) 166 | * The difference between the 2 positions can then be compared regularly to check 167 | * for an external step loss. 168 | */ 169 | void setEncoderLatching(bool enabled); 170 | 171 | /* Set maximum number of steps between internal position and encoder position 172 | * before triggering the deviation flag. 173 | * Set to 0 to disable. */ 174 | void setEncoderAllowedDeviation(int steps); 175 | 176 | /* Check if a deviation between internal pos and encoder has been detected */ 177 | bool isEncoderDeviationDetected(); 178 | 179 | /* Clear encoder deviation flag (deviation condition must be handled before) */ 180 | void clearEncoderDeviationFlag(); 181 | 182 | //TODO end stops and stallguard config functions ? 183 | 184 | /* Configure the integrated short protection. Check datasheet for details. 185 | * - s2vsLevel : 4 (highest sensitivity) to 15 ; 6 to 8 recommended ; reset default 6 186 | * - s2gLevel : 2 (highest sensitivity) to 15 ; 6 to 14 recommended ; reset default 6 ; increase at higher voltage 187 | * - shortFilter : 0 to 3 ; reset default 1 ; increase in case of erroneous detection 188 | * - shortDelay : 0 to 1 ; reset default 0 189 | */ 190 | void setShortProtectionLevels(int s2vsLevel, int s2gLevel, int shortFilter, int shortDelay = 0); 191 | 192 | protected: 193 | static constexpr uint8_t WRITE_ACCESS = 0x80; //Register write access for spi / uart communication 194 | 195 | bool _lastRegisterReadSuccess = false; 196 | const uint32_t _fclk; 197 | 198 | private: 199 | RampMode _currentRampMode; 200 | static constexpr uint16_t _uStepCount = 256; // Number of microsteps per step 201 | TMC5160_Reg::CHOPCONF_Register _chopConf = { 0 }; //CHOPCONF register (saved here to be restored when disabling / enabling driver) 202 | 203 | // Following §14.1 Real world unit conversions 204 | // v[Hz] = v[5160A] * ( f CLK [Hz]/2 / 2^23 ) 205 | float speedToHz(int32_t speedInternal) { return ((float)speedInternal * (float)_fclk / (float)(1ul << 24) / (float)_uStepCount); } 206 | int32_t speedFromHz(float speedHz) { return (int32_t)(speedHz / ((float)_fclk / (float)(1ul << 24)) * (float)_uStepCount); } 207 | 208 | // Following §14.1 Real world unit conversions 209 | // a[Hz/s] = a[5160A] * f CLK [Hz]^2 / (512*256) / 2^24 210 | int32_t accelFromHz(float accelHz) { return (int32_t)(accelHz / ((float)_fclk * (float)_fclk / (512.0*256.0) / (float)(1ul<<24)) * (float)_uStepCount); } 211 | 212 | // See §12 Velocity based mode control 213 | int32_t thrsSpeedToTstep(float thrsSpeed) { return thrsSpeed != 0.0 ? (int32_t)constrain((float)_fclk / (thrsSpeed * 256.0), 0, 1048575) : 0; } 214 | }; 215 | 216 | 217 | /* SPI interface :  218 | * the TMC5160 SWSEL input has to be low (default state). 219 | */ 220 | class TMC5160_SPI : public TMC5160 { 221 | public: 222 | TMC5160_SPI( uint8_t chipSelectPin, // pin to use for the SPI bus SS line 223 | uint32_t fclk = DEFAULT_F_CLK, 224 | const SPISettings &spiSettings = SPISettings(4000000, MSBFIRST, SPI_MODE3), // spi bus settings to use (max SCK frequency of 4Mhz) 225 | SPIClass& spi = SPI ); // spi class to use 226 | 227 | uint32_t readRegister(uint8_t address); // addresses are from TMC5160.h 228 | uint8_t writeRegister(uint8_t address, uint32_t data); 229 | uint8_t readStatus(); 230 | 231 | private: 232 | uint8_t _CS; 233 | SPISettings _spiSettings; 234 | SPIClass *_spi; 235 | 236 | void _beginTransaction(); 237 | void _endTransaction(); 238 | }; 239 | 240 | 241 | /* Generic UART interface */ 242 | class TMC5160_UART_Generic : public TMC5160 { 243 | public: 244 | /* Read/write register return codes */ 245 | enum ReadStatus {SUCCESS, NO_REPLY, INVALID_FORMAT, BAD_CRC}; 246 | 247 | /* Serial communication modes. In reliable mode, register writes are checked and 248 | * retried if necessary, and register reads are retried multiple times in case 249 | * of failure. In streaming mode, none of these checks are performed and register 250 | * read / writes are tried only once. Default is Streaming mode. */ 251 | enum CommunicationMode {RELIABLE_MODE, STREAMING_MODE}; 252 | 253 | 254 | TMC5160_UART_Generic(uint8_t slaveAddress = 0, // TMC5160 slave address (default 0 if NAI is low, 1 if NAI is high) 255 | uint32_t baudRate = 500000, // UART baud rate (necessary to compute certain delays) 256 | uint32_t fclk = DEFAULT_F_CLK); // TMC5160 clock freq 257 | 258 | virtual bool begin(PowerStageParameters &powerParams, MotorParameters &motorParams, MotorDirection stepperDirection/*=NORMAL_MOTOR_DIRECTION*/); 259 | 260 | uint32_t readRegister(uint8_t address, ReadStatus *status); // addresses are from TMC5160.h. Pass an optional status pointer to detect failures. 261 | uint32_t readRegister(uint8_t address) { return readRegister(address, nullptr); } 262 | uint8_t writeRegister(uint8_t address, uint32_t data, ReadStatus *status); // Pass an optional status pointer to detect failures. 263 | uint8_t writeRegister(uint8_t address, uint32_t data) { return writeRegister(address, data, nullptr); } 264 | 265 | void resetCommunication(); // Reset communication with TMC5160 : pause activity on the serial bus. 266 | 267 | void setSlaveAddress(uint8_t slaveAddress, bool NAI=true); // Set the slave address register. Take into account the TMC5160 NAI input (default to high). Range : 0 - 253 if NAI is low, 1 - 254 if NAI is high. 268 | void setInternalSlaveAddress(uint8_t slaveAddress) { _slaveAddress = slaveAddress; } 269 | uint8_t getSlaveAddress() { return _slaveAddress; } 270 | 271 | void setCommunicationMode(CommunicationMode mode); 272 | 273 | /* Register read / write statistics */ 274 | void resetCommunicationSuccessRate(); 275 | float getReadSuccessRate(); 276 | float getWriteSuccessRate(); 277 | protected: 278 | static constexpr uint8_t NB_RETRIES_READ = 3; 279 | static constexpr uint8_t NB_RETRIES_WRITE = 3; 280 | 281 | uint8_t _slaveAddress; 282 | CommunicationMode _currentMode; 283 | uint8_t _transmissionCounter; 284 | uint32_t _uartBaudRate; 285 | 286 | /* Read / write fail statistics */ 287 | uint32_t _readAttemptsCounter = 0; 288 | uint32_t _readSuccessfulCounter = 0; 289 | uint32_t _writeAttemptsCounter = 0; 290 | uint32_t _writeSuccessfulCounter = 0; 291 | 292 | 293 | virtual void beginTransmission() {} 294 | virtual void endTransmission() {} 295 | 296 | virtual void uartFlushInput() = 0; 297 | virtual void uartWriteBytes(const uint8_t *buf, uint8_t len) = 0; 298 | virtual int uartReadBytes(uint8_t *buf, uint8_t len) = 0; 299 | virtual uint8_t uartReadByte() = 0; 300 | virtual int uartBytesAvailable() = 0; 301 | 302 | uint32_t _readReg(uint8_t address, ReadStatus *status); 303 | void _writeReg(uint8_t address, uint32_t data); 304 | 305 | void delayBitTimes(uint16_t bits); 306 | 307 | private: 308 | static constexpr uint8_t SYNC_BYTE = 0x05; 309 | static constexpr uint8_t MASTER_ADDRESS = 0xFF; 310 | 311 | void computeCrc(uint8_t *datagram, uint8_t datagramLength); 312 | }; 313 | 314 | 315 | /* Arduino UART interface : 316 | * the TMC5160 SD_MODE and SPI_MODE inputs must be tied low. 317 | * 318 | * This class does not handle TX/RX switch on the half-duplex bus. 319 | * It should be used only if there is another mechanism to switch between 320 | * transmission and reception (e.g. on Teensy the Serial class can be configured 321 | * to control an external transceiver). 322 | * 323 | * It is not advised to use this class directly. Use TMC5160_UART_Transceiver instead 324 | * as it provides a better control of the transceiver enable. The TMC5160 sometimes 325 | * requires the bus to be in an stable state for a given duration (bus reset time) 326 | * before accepting commands. 327 | * 328 | * Serial must be initialized externally. Serial.setTimeout() must be set to a 329 | * decent value to avoid blocking for too long if there is a RX error. 330 | */ 331 | class TMC5160_UART : public TMC5160_UART_Generic { 332 | public: 333 | TMC5160_UART(Stream& serial = Serial, // Serial port to use 334 | uint8_t slaveAddress = 0, // TMC5160 slave address (default 0 if NAI is low, 1 if NAI is high) 335 | uint32_t baudRate = 500000, // UART baud rate (necessary to compute certain delays) 336 | uint32_t fclk = DEFAULT_F_CLK) : // TMC5160 clock freq 337 | TMC5160_UART_Generic(slaveAddress, baudRate, fclk), _serial(&serial) 338 | { } 339 | 340 | protected: 341 | Stream *_serial; 342 | 343 | virtual void uartFlushInput() 344 | { 345 | while (_serial->available()) 346 | _serial->read(); 347 | } 348 | 349 | virtual void uartWriteBytes(const uint8_t *buf, uint8_t len) 350 | { 351 | _serial->write(buf, len); 352 | } 353 | 354 | virtual int uartReadBytes(uint8_t *buf, uint8_t len) 355 | { 356 | return _serial->readBytes(buf, len); 357 | } 358 | 359 | virtual uint8_t uartReadByte() 360 | { 361 | return (uint8_t)(_serial->read()); 362 | } 363 | 364 | virtual int uartBytesAvailable() 365 | { 366 | return _serial->available(); 367 | } 368 | }; 369 | 370 | /* Arduino UART interface with external transceiver support : 371 | * the TMC5160 SD_MODE and SPI_MODE inputs must be tied low. 372 | * See TMC5160 datasheet §5.4 figure 5.2 for wiring details 373 | * 374 | * This interface switches a digital pin to control an external transceiver to 375 | * free the bus when not transmitting. 376 | * 377 | * Serial must be initialized externally. Serial.setTimeout() must be set to a 378 | * decent value to avoid blocking for too long if there is a RX error. 379 | */ 380 | class TMC5160_UART_Transceiver : public TMC5160_UART { 381 | public: 382 | TMC5160_UART_Transceiver(uint8_t txEnablePin = -1, // pin to enable transmission on the external transceiver 383 | Stream& serial = Serial, // Serial port to use 384 | uint8_t slaveAddress = 0, // TMC5160 slave address (default 0 if NAI is low, 1 if NAI is high) 385 | uint32_t baudRate = 500000, // UART baud rate (necessary to compute certain delays) 386 | uint32_t fclk = DEFAULT_F_CLK) // TMC5160 clock freq 387 | : TMC5160_UART(serial, slaveAddress, baudRate, fclk), _txEn(txEnablePin) 388 | { 389 | pinMode(_txEn, OUTPUT); 390 | } 391 | 392 | protected: 393 | void beginTransmission() 394 | { 395 | digitalWrite(_txEn, HIGH); 396 | delayBitTimes(63+12+4); // Some ICs are more sensitive and need a communication reset time between 2 read/write accesses. 397 | } 398 | 399 | void endTransmission() 400 | { 401 | _serial->flush(); 402 | digitalWrite(_txEn, LOW); 403 | } 404 | 405 | private: 406 | uint8_t _txEn; 407 | }; 408 | 409 | 410 | #endif // TMC5160_H 411 | -------------------------------------------------------------------------------- /src/TMC5160_SPI.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Mike Estee 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include "TMC5160.h" 26 | 27 | TMC5160_SPI::TMC5160_SPI( uint8_t chipSelectPin, uint32_t fclk, const SPISettings &spiSettings, SPIClass &spi ) 28 | : TMC5160(fclk), _CS(chipSelectPin), _spiSettings(spiSettings), _spi(&spi) 29 | { 30 | pinMode(_CS, OUTPUT); 31 | digitalWrite(_CS, HIGH); 32 | } 33 | 34 | void TMC5160_SPI::_beginTransaction() 35 | { 36 | _spi->beginTransaction(_spiSettings); 37 | digitalWrite(_CS, LOW); 38 | } 39 | 40 | void TMC5160_SPI::_endTransaction() 41 | { 42 | digitalWrite(_CS, HIGH); 43 | _spi->endTransaction(); 44 | } 45 | 46 | uint32_t TMC5160_SPI::readRegister(uint8_t address) 47 | { 48 | // request the read for the address 49 | _beginTransaction(); 50 | _spi->transfer(address); 51 | _spi->transfer(0x00); 52 | _spi->transfer(0x00); 53 | _spi->transfer(0x00); 54 | _spi->transfer(0x00); 55 | _endTransaction(); 56 | 57 | //Delay for the minimum CSN high time (2tclk + 10ns -> 176ns with the default 12MHz clock) 58 | #ifdef TEENSYDUINO 59 | delayNanoseconds(2 * 1000000000 / _fclk + 10); 60 | #else 61 | delayMicroseconds(1); 62 | #endif 63 | 64 | // read it in the second cycle 65 | _beginTransaction(); 66 | _spi->transfer(address); 67 | uint32_t value = 0; 68 | value |= (uint32_t)_spi->transfer(0x00) << 24; 69 | value |= (uint32_t)_spi->transfer(0x00) << 16; 70 | value |= (uint32_t)_spi->transfer(0x00) << 8; 71 | value |= (uint32_t)_spi->transfer(0x00); 72 | _endTransaction(); 73 | 74 | _lastRegisterReadSuccess = true; // In SPI mode there is no way to know if the TMC5130 is plugged... 75 | 76 | return value; 77 | } 78 | 79 | uint8_t TMC5160_SPI::writeRegister(uint8_t address, uint32_t data) 80 | { 81 | // address register 82 | _beginTransaction(); 83 | uint8_t status = _spi->transfer(address | WRITE_ACCESS); 84 | 85 | // send new register value 86 | _spi->transfer((data & 0xFF000000) >> 24); 87 | _spi->transfer((data & 0xFF0000) >> 16); 88 | _spi->transfer((data & 0xFF00) >> 8); 89 | _spi->transfer(data & 0xFF); 90 | _endTransaction(); 91 | 92 | return status; 93 | } 94 | 95 | 96 | uint8_t TMC5160_SPI::readStatus() 97 | { 98 | // read general config 99 | _beginTransaction(); 100 | uint8_t status = _spi->transfer(TMC5160_Reg::GCONF); 101 | // send dummy data 102 | _spi->transfer(0x00); 103 | _spi->transfer(0x00); 104 | _spi->transfer(0x00); 105 | _spi->transfer(0x00); 106 | _endTransaction(); 107 | 108 | return status; 109 | } 110 | -------------------------------------------------------------------------------- /src/TMC5160_UART.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2017 Tom Magnier 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include "TMC5160.h" 26 | 27 | // #define SERIAL_PRINT_ERRORS 28 | // #define SERIAL_DEBUG 29 | 30 | TMC5160_UART_Generic::TMC5160_UART_Generic(uint8_t slaveAddress, uint32_t baudRate, uint32_t fclk) 31 | : TMC5160(fclk), _slaveAddress(slaveAddress), _currentMode(STREAMING_MODE), _uartBaudRate(baudRate) 32 | { 33 | 34 | } 35 | 36 | bool TMC5160_UART_Generic::begin(PowerStageParameters &powerParams, MotorParameters &motorParams, MotorDirection stepperDirection) 37 | { 38 | CommunicationMode oldMode = _currentMode; 39 | setCommunicationMode(RELIABLE_MODE); 40 | 41 | bool result = TMC5160::begin(powerParams, motorParams, stepperDirection); 42 | setCommunicationMode(oldMode); 43 | return result; 44 | } 45 | 46 | uint32_t TMC5160_UART_Generic::readRegister(uint8_t address, ReadStatus *status) 47 | { 48 | uint32_t data = 0xFFFFFFFF; 49 | ReadStatus readStatus = NO_REPLY; //Worst case. 50 | 51 | switch (_currentMode) 52 | { 53 | case STREAMING_MODE: 54 | data = _readReg(address, &readStatus); 55 | break; 56 | 57 | case RELIABLE_MODE: 58 | { 59 | int retries = NB_RETRIES_READ; 60 | do { 61 | ReadStatus trialStatus; 62 | data = _readReg(address, &trialStatus); 63 | 64 | if (trialStatus == SUCCESS || (readStatus == NO_REPLY && trialStatus != NO_REPLY)) 65 | readStatus = trialStatus; 66 | 67 | retries--; 68 | } while (readStatus != SUCCESS && retries > 0); 69 | break; 70 | } 71 | } 72 | 73 | if (status != nullptr) 74 | *status = readStatus; 75 | 76 | _lastRegisterReadSuccess = (readStatus == SUCCESS); 77 | 78 | return data; 79 | } 80 | 81 | uint8_t TMC5160_UART_Generic::writeRegister(uint8_t address, uint32_t data, ReadStatus *status) 82 | { 83 | switch (_currentMode) 84 | { 85 | case STREAMING_MODE: 86 | _writeReg(address, data); 87 | 88 | if (status != nullptr) 89 | *status = SUCCESS; 90 | break; 91 | 92 | case RELIABLE_MODE: 93 | { 94 | int retries = NB_RETRIES_WRITE; 95 | ReadStatus writeStatus = NO_REPLY; 96 | do { 97 | _writeReg(address, data); 98 | _writeAttemptsCounter++; 99 | 100 | ReadStatus readStatus; 101 | uint8_t counter = readRegister(TMC5160_Reg::IFCNT, &readStatus) & 0xFF; 102 | 103 | if (readStatus != NO_REPLY) 104 | writeStatus = readStatus; 105 | 106 | if (readStatus == SUCCESS) 107 | { 108 | if (counter != _transmissionCounter + 1) 109 | writeStatus = BAD_CRC; 110 | 111 | _transmissionCounter = counter; 112 | } 113 | 114 | retries--; 115 | } while (writeStatus != SUCCESS && retries > 0); 116 | 117 | if (status != nullptr) 118 | *status = writeStatus; 119 | 120 | if (writeStatus == SUCCESS) 121 | _writeSuccessfulCounter++; 122 | 123 | break; 124 | } 125 | } 126 | 127 | return 0; 128 | } 129 | 130 | void TMC5160_UART_Generic::resetCommunication() 131 | { 132 | //Reset communication : see datasheet section 5. 133 | //The bus must stay idle for 63 bit times then 12 bit times of recovery. 134 | delayBitTimes(63 + 12 + 4); // add 4 bit times of margin 135 | 136 | #ifdef SERIAL_DEBUG 137 | Serial.println("Resetting communication."); 138 | #endif 139 | 140 | uartFlushInput(); 141 | } 142 | 143 | void TMC5160_UART_Generic::setSlaveAddress(uint8_t slaveAddress, bool NAI) 144 | { 145 | TMC5160_Reg::SLAVECONF_Register slaveConf = { 0 }; 146 | slaveConf.senddelay = 2; // minimum if more than one slave is present. 147 | slaveConf.slaveaddr = constrain(NAI ? slaveAddress-1 : slaveAddress, 0, 253); //NB : if NAI is high SLAVE_ADDR is incremented. 148 | 149 | writeRegister(TMC5160_Reg::SLAVECONF, slaveConf.value); 150 | 151 | _slaveAddress = NAI ? slaveConf.slaveaddr+1 : slaveConf.slaveaddr; 152 | } 153 | 154 | void TMC5160_UART_Generic::setCommunicationMode(TMC5160_UART_Generic::CommunicationMode mode) 155 | { 156 | if (_currentMode == mode) 157 | return; 158 | 159 | _currentMode = mode; 160 | 161 | if (mode == RELIABLE_MODE) 162 | { 163 | //Initialize the 8-bit transmission counter. 164 | _transmissionCounter = readRegister(TMC5160_Reg::IFCNT) & 0xFF; 165 | } 166 | } 167 | 168 | void TMC5160_UART_Generic::resetCommunicationSuccessRate() 169 | { 170 | _readAttemptsCounter = _readSuccessfulCounter = _writeAttemptsCounter = _writeSuccessfulCounter = 0; 171 | } 172 | 173 | float TMC5160_UART_Generic::getReadSuccessRate() 174 | { 175 | if (_readAttemptsCounter == 0) 176 | return 0; 177 | 178 | return (float)_readSuccessfulCounter / (float)_readAttemptsCounter; 179 | } 180 | 181 | float TMC5160_UART_Generic::getWriteSuccessRate() 182 | { 183 | if (_writeAttemptsCounter == 0) 184 | return 0; 185 | 186 | return (float)_writeSuccessfulCounter / (float)_writeAttemptsCounter; 187 | } 188 | 189 | uint32_t TMC5160_UART_Generic::_readReg(uint8_t address, ReadStatus *status) 190 | { 191 | uint8_t outBuffer[4], inBuffer[8]; 192 | 193 | outBuffer[0] = SYNC_BYTE; 194 | outBuffer[1] = _slaveAddress; 195 | outBuffer[2] = address; 196 | 197 | computeCrc(outBuffer, 4); 198 | 199 | uartFlushInput(); 200 | 201 | beginTransmission(); 202 | uartWriteBytes(outBuffer, 4); 203 | endTransmission(); 204 | 205 | _readAttemptsCounter++; 206 | 207 | unsigned long startTime = micros(); 208 | uint8_t rxLen = 0; 209 | // Timeout value : the TMC5160 starts replying 3*8 bit times after the transmission end (SENDDELAY = 2) 210 | // Then 8 bytes are transmitted, each with its start and stop bits => 8*10 bit times 211 | // => Timeout value : 3*8 + 8*10 + 10 (margin) bit times 212 | unsigned long timeoutBits = 3*8 + 8*10 + 10; 213 | // A higher margin is necessary for ESP32 as the UART peripheral inserts an idle time of 256 bits 214 | // between transmissions -> data is not pushed out immediately. 215 | // Worst case is an additionnal 256 bit delay - the transmission of the previous message (min 4 bytes => 40 bit times) 216 | #ifdef ESP_PLATFORM 217 | timeoutBits += 216; 218 | #endif 219 | 220 | while (micros() - startTime < (1000000ul * timeoutBits / _uartBaudRate) && rxLen < 8) 221 | { 222 | if (uartBytesAvailable() > 0) 223 | { 224 | inBuffer[rxLen++] = uartReadByte(); 225 | //Discard the first bytes if necessary 226 | if (rxLen == 1 && inBuffer[0] != SYNC_BYTE) 227 | rxLen = 0; 228 | } 229 | } 230 | 231 | if (rxLen < 8) 232 | { 233 | if (status != nullptr) 234 | *status = NO_REPLY; 235 | 236 | #ifdef SERIAL_PRINT_ERRORS 237 | Serial.print("Read 0x"); 238 | Serial.print(address, HEX); 239 | Serial.print(": No reply ("); 240 | Serial.print(rxLen); 241 | Serial.println(" bytes read)"); 242 | Serial.print("{"); 243 | for (int i = 0; i < 8; i++) 244 | { 245 | Serial.print("0x"); 246 | Serial.print(inBuffer[i], HEX); 247 | Serial.print(" "); 248 | } 249 | Serial.println("}"); 250 | #endif 251 | 252 | return 0xFFFFFFFF; 253 | } 254 | 255 | if (inBuffer[0] != SYNC_BYTE || inBuffer[1] != MASTER_ADDRESS || inBuffer[2] != address) 256 | { 257 | if (status != nullptr) 258 | *status = INVALID_FORMAT; 259 | 260 | #ifdef SERIAL_PRINT_ERRORS 261 | Serial.print("Read 0x"); 262 | Serial.print(address, HEX); 263 | Serial.println(": Invalid answer format."); 264 | Serial.print("{"); 265 | for (int i = 0; i < 8; i++) 266 | { 267 | Serial.print("0x"); 268 | Serial.print(inBuffer[i], HEX); 269 | Serial.print(" "); 270 | } 271 | Serial.println("}"); 272 | #endif 273 | 274 | uartFlushInput(); 275 | 276 | return 0xFFFFFFFF; 277 | } 278 | 279 | uint8_t receivedCrc = inBuffer[7]; 280 | computeCrc(inBuffer, 8); 281 | 282 | if (receivedCrc != inBuffer[7]) 283 | { 284 | if (status != nullptr) 285 | *status = BAD_CRC; 286 | 287 | #ifdef SERIAL_PRINT_ERRORS 288 | Serial.print("Read 0x"); 289 | Serial.print(address, HEX); 290 | Serial.println(": Bad CRC."); 291 | #endif 292 | 293 | return 0xFFFFFFFF; 294 | } 295 | 296 | uint32_t data = 0; 297 | for (int i = 0; i < 4; i++) 298 | data += ((uint32_t)inBuffer[3+i] << ((3-i)*8)); 299 | 300 | #ifdef SERIAL_DEBUG 301 | Serial.print("Read 0x"); 302 | Serial.print(address, HEX); 303 | Serial.print(": 0x"); 304 | Serial.println(data, HEX); 305 | #endif 306 | 307 | _readSuccessfulCounter++; 308 | 309 | if (status != nullptr) 310 | *status = SUCCESS; 311 | 312 | return data; 313 | } 314 | 315 | void TMC5160_UART_Generic::_writeReg(uint8_t address, uint32_t data) 316 | { 317 | #ifdef SERIAL_DEBUG 318 | Serial.print("Writing 0x"); 319 | Serial.print(address, HEX); 320 | Serial.print(": 0x"); 321 | Serial.println(data, HEX); 322 | #endif 323 | 324 | uint8_t buffer[8]; 325 | buffer[0] = SYNC_BYTE; 326 | buffer[1] = _slaveAddress; 327 | buffer[2] = address | WRITE_ACCESS; 328 | for (int i = 0; i < 4; i++) 329 | buffer[3+i] = (data & (0xFFul << ((3-i)*8))) >> ((3-i)*8); 330 | 331 | computeCrc(buffer, 8); 332 | 333 | #if 0 334 | //Intentional interference to test the reliable mode : change the CRC 335 | if (random(256) < 64) 336 | buffer[7]++; 337 | #endif 338 | 339 | beginTransmission(); 340 | uartWriteBytes(buffer, 8); 341 | endTransmission(); 342 | } 343 | 344 | void TMC5160_UART_Generic::delayBitTimes(uint16_t bits) 345 | { 346 | delayMicroseconds(1000000ul * bits / _uartBaudRate ); 347 | } 348 | 349 | /* From Trinamic TMC5130A datasheet Rev. 1.14 / 2017-MAY-15 §5.2 */ 350 | void TMC5160_UART_Generic::computeCrc(uint8_t *datagram, uint8_t datagramLength) 351 | { 352 | int i,j; 353 | uint8_t* crc = datagram + (datagramLength-1); // CRC located in last byte of message 354 | uint8_t currentByte; 355 | 356 | *crc = 0; 357 | for (i = 0; i < (datagramLength-1); i++) 358 | { 359 | currentByte = datagram[i]; 360 | for (j = 0; j < 8; j++) 361 | { 362 | if ((*crc >> 7) ^ (currentByte & 0x01)) 363 | *crc = (*crc << 1) ^ 0x07; 364 | else 365 | *crc = (*crc << 1); 366 | 367 | currentByte = currentByte >> 1; 368 | } // for CRC bit 369 | } // for message byte 370 | } 371 | -------------------------------------------------------------------------------- /src/TMC5160_registers.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2017 Tom Magnier 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef TMC5160_REGISTERS_H 26 | #define TMC5160_REGISTERS_H 27 | 28 | #include 29 | 30 | namespace TMC5160_Reg { 31 | 32 | /* Register addresses */ 33 | enum { 34 | /* General configuration registers */ 35 | GCONF = 0x00, // Global configuration flags 36 | GSTAT = 0x01, // Global status flags 37 | IFCNT = 0x02, // UART transmission counter 38 | SLAVECONF = 0x03, // UART slave configuration 39 | IO_INPUT_OUTPUT = 0x04, // Read input / write output pins 40 | X_COMPARE = 0x05, // Position comparison register 41 | OTP_PROG = 0x06, // OTP programming register 42 | OTP_READ = 0x07, // OTP read register 43 | FACTORY_CONF = 0x08, // Factory configuration (clock trim) 44 | SHORT_CONF = 0x09, // Short detector configuration 45 | DRV_CONF = 0x0A, // Driver configuration 46 | GLOBAL_SCALER = 0x0B, // Global scaling of motor current 47 | OFFSET_READ = 0x0C, // Offset calibration results 48 | 49 | /* Velocity dependent driver feature control registers */ 50 | IHOLD_IRUN = 0x10, // Driver current control 51 | TPOWERDOWN = 0x11, // Delay before power down 52 | TSTEP = 0x12, // Actual time between microsteps 53 | TPWMTHRS = 0x13, // Upper velocity for stealthChop voltage PWM mode 54 | TCOOLTHRS = 0x14, // Lower threshold velocity for switching on smart energy coolStep and stallGuard feature 55 | THIGH = 0x15, // Velocity threshold for switching into a different chopper mode and fullstepping 56 | 57 | /* Ramp generator motion control registers */ 58 | RAMPMODE = 0x20, // Driving mode (Velocity, Positioning, Hold) 59 | XACTUAL = 0x21, // Actual motor position 60 | VACTUAL = 0x22, // Actual motor velocity from ramp generator 61 | VSTART = 0x23, // Motor start velocity 62 | A_1 = 0x24, // First acceleration between VSTART and V1 63 | V_1 = 0x25, // First acceleration/deceleration phase target velocity 64 | AMAX = 0x26, // Second acceleration between V1 and VMAX 65 | VMAX = 0x27, // Target velocity in velocity mode 66 | DMAX = 0x28, // Deceleration between VMAX and V1 67 | D_1 = 0x2A, // Deceleration between V1 and VSTOP 68 | //Attention: Do not set 0 in positioning mode, even if V1=0! 69 | VSTOP = 0x2B, // Motor stop velocity 70 | //Attention: Set VSTOP > VSTART! 71 | //Attention: Do not set 0 in positioning mode, minimum 10 recommend! 72 | TZEROWAIT = 0x2C, // Waiting time after ramping down to zero velocity before next movement or direction inversion can start. 73 | XTARGET = 0x2D, // Target position for ramp mode 74 | 75 | /* Ramp generator driver feature control registers */ 76 | VDCMIN = 0x33, // Velocity threshold for enabling automatic commutation dcStep 77 | SW_MODE = 0x34, // Switch mode configuration 78 | RAMP_STAT = 0x35, // Ramp status and switch event status 79 | XLATCH = 0x36, // Ramp generator latch position upon programmable switch event 80 | 81 | /* Encoder registers */ 82 | ENCMODE = 0x38, // Encoder configuration and use of N channel 83 | X_ENC = 0x39, // Actual encoder position 84 | ENC_CONST = 0x3A, // Accumulation constant 85 | ENC_STATUS = 0x3B, // Encoder status information 86 | ENC_LATCH = 0x3C, // Encoder position latched on N event 87 | ENC_DEVIATION = 0x3D, // Maximum number of steps deviation between encoder counter and XACTUAL for deviation warning 88 | 89 | /* Motor driver registers */ 90 | MSLUT_0_7 = 0x60, // Microstep table entries. Add 0...7 for the next registers 91 | MSLUTSEL = 0x68, // Look up table segmentation definition 92 | MSLUTSTART = 0x69, // Absolute current at microstep table entries 0 and 256 93 | MSCNT = 0x6A, // Actual position in the microstep table 94 | MSCURACT = 0x6B, // Actual microstep current 95 | CHOPCONF = 0x6C, // Chopper and driver configuration 96 | COOLCONF = 0x6D, // coolStep smart current control register and stallGuard2 configuration 97 | DCCTRL = 0x6E, // dcStep automatic commutation configuration register 98 | DRV_STATUS = 0x6F, // stallGuard2 value and driver error flags 99 | PWMCONF = 0x70, // stealthChop voltage PWM mode chopper configuration 100 | PWM_SCALE = 0x71, // Results of stealthChop amplitude regulator. 101 | PWM_AUTO = 0x72, // Automatically determined PWM config values 102 | LOST_STEPS = 0x73 // Number of input steps skipped due to dcStep. only with SD_MODE = 1 103 | }; 104 | 105 | /* Register bit fields */ 106 | 107 | /* General configuration register */ 108 | union GCONF_Register { 109 | uint32_t value; 110 | BitField< 0> recalibrate; // Zero crossing recalibration during driver disable 111 | BitField< 1> faststandstill; // Timeout for step execution until standstill detection 112 | BitField< 2> en_pwm_mode; // Enable stealthChop voltage PWM mode 113 | BitField< 3> multistep_filt; // Enable step input filtering for stealthChop optimization with external step source 114 | BitField< 4> shaft; // Normal / inverse motor direction 115 | BitField< 5> diag0_error; // Enable DIAG0 active on driver errors: Over temperature (ot), short to GND (s2g), undervoltage chargepump (uv_cp) 116 | BitField< 6> diag0_otpw; // Enable DIAG0 active on driver over temperature prewarning (otpw) 117 | BitField< 7> diag0_stall_step; // SD_MODE=1: enable DIAG0 active on motor stall. SD_MODE=0: enable DIAG0 as STEP output 118 | BitField< 8> diag1_stall_dir; // SD_MODE=1: enable DIAG1 active on motor stall. SD_MODE=0: enable DIAG1 as DIR output 119 | BitField< 9> diag1_index; // Enable DIAG1 active on index position 120 | BitField<10> diag1_onstate; // Enable DIAG1 active when chopper is on 121 | BitField<11> diag1_steps_skipped; // Enable output toggle when steps are skipped in dcStep mode 122 | BitField<12> diag0_int_pushpull; // Enable SWN_DIAG0 push pull output 123 | BitField<13> diag1_poscomp_pushpull; // Enable SWP_DIAG1 push pull output 124 | BitField<14> small_hysteresis; // Set small hysteresis for step frequency comparison 125 | BitField<15> stop_enable; // Enable emergency stop: ENCA_DCIN stops the sequencer when tied high 126 | BitField<16> direct_mode; // Enable direct motor coil current and polarity control 127 | BitField<17> test_mode; // Not for normal use 128 | }; 129 | 130 | /* Global status flags */ 131 | union GSTAT_Register { 132 | uint32_t value; 133 | BitField<0> reset; // Indicates that the IC has been reset since the last read access to GSTAT 134 | BitField<1> drv_err; // Indicates that the driver has been shut down due to overtemperature or short circuit detection since the last read access 135 | BitField<2> uv_cp; // Indicates an undervoltage on the charge pump. The driver is disabled in this case. 136 | }; 137 | 138 | /* UART slave configuration */ 139 | union SLAVECONF_Register { 140 | uint32_t value; 141 | BitField<0, 8> slaveaddr; // Address of unit for the UART interface. The address becomes incremented by one when the external address pin NAI is active. 142 | BitField<8, 4> senddelay; // Number of bit times before replying to a register read in UART mode. Set > 1 with multiple slaves. 143 | }; 144 | 145 | /* Read input pins */ 146 | union IOIN_Register { 147 | uint32_t value; 148 | BitField<0> refl_step; 149 | BitField<1> refr_dir; 150 | BitField<2> encb_dcen_cfg4; 151 | BitField<3> enca_dcin_cfg5; 152 | BitField<4> drv_enn; 153 | BitField<5> enc_n_dco_cfg6; 154 | BitField<6> sd_mode; // 1=External step and dir source 155 | BitField<7> swcomp_in; 156 | BitField<24, 8> version; 157 | }; 158 | 159 | /* OTP programming */ 160 | union OTP_PROG_Register { 161 | uint32_t value; 162 | BitField<0, 3> otpbit; // Selection of OTP bit to be programmed 163 | BitField<4, 2> otpbyte; // Selection of OTP byte. Set to 00 164 | BitField<8, 8> otpmagic; // Set to 0xBD to program. 165 | }; 166 | 167 | /* OTP configuration memory */ 168 | union OTP_READ_Register { 169 | uint32_t value; 170 | BitField<0, 5> otp_fclktrim; // Reset default for FCLKTRIM (frequency source calibration) 171 | BitField<5> otp_S2_level; // Reset default for Short detection levels 172 | BitField<6> otp_bbm; // Reset default for DRVCONF.BBMCLKS 173 | BitField<7> otp_tbl; // Reset default for TBL 174 | }; 175 | 176 | /* Short detector configuration */ 177 | union SHORT_CONF_Register { 178 | uint32_t value; 179 | BitField< 0, 4> s2vs_level; // Short to VS detector for low side FETs sensitivity 180 | BitField< 8, 4> s2g_level; // Short to GND detector for high side FETs sensitivity 181 | BitField<16, 2> shortfilter; // Spike filtering bandwidth for short detection 182 | BitField<18> shortdelay; // Short detection delay 183 | }; 184 | 185 | /* Driver configuration */ 186 | union DRV_CONF_Register { 187 | uint32_t value; 188 | BitField< 0, 5> bbmtime; // Break before make delay (0 to 24) 189 | BitField< 8, 4> bbmclks; // Digital BBM Time in clock cycles 190 | BitField<16, 2> otselect; // Selection of over temperature level for bridge disable 191 | BitField<18, 2> drvstrength; // Selection of gate drivers current 192 | BitField<20, 2> filt_isense; // Filter time constant of sense amplifier to suppress ringing and coupling from second coil operation 193 | }; 194 | 195 | /* Offset calibration result */ 196 | union OFFSET_READ_Register { 197 | uint32_t value; 198 | BitField<0, 8> phase_b; 199 | BitField<8, 8> phase_a; 200 | }; 201 | 202 | /* Driver current control */ 203 | union IHOLD_IRUN_Register { 204 | uint32_t value; 205 | BitField< 0, 5> ihold; // Standstill current (0=1/32...31=32/32) 206 | BitField< 8, 5> irun; // Motor run current (0=1/32...31=32/32). Should be between 16 and 31 for best performance. 207 | BitField<16, 4> iholddelay; // Controls the number of clock cycles for motor power down when entering standstill 208 | }; 209 | 210 | /* Switch mode configuration */ 211 | union SW_MODE_Register { 212 | uint32_t value; 213 | BitField< 0> stop_l_enable; // Enable automatic motor stop during active left reference switch input 214 | BitField< 1> stop_r_enable; // Enable automatic motor stop during active right reference switch input 215 | BitField< 2> pol_stop_l; // Sets the active polarity of the left reference switch input (1=inverted, low active, a low level on REFL stops the motor) 216 | BitField< 3> pol_stop_r; // Sets the active polarity of the right reference switch input (1=inverted, low active, a low level on REFR stops the motor 217 | BitField< 4> swap_lr; // Swap the left and the right reference switch inputs 218 | BitField< 5> latch_l_active; // Activate latching of the position to XLATCH upon an active going edge on REFL 219 | BitField< 6> latch_l_inactive; // Activate latching of the position to XLATCH upon an inactive going edge on REFL 220 | BitField< 7> latch_r_active; // Activate latching of the position to XLATCH upon an active going edge on REFR 221 | BitField< 8> latch_r_inactive; // Activate latching of the position to XLATCH upon an inactive going edge on REFR 222 | BitField< 9> en_latch_encoder; // Latch encoder position to ENC_LATCH upon reference switch event 223 | BitField<10> sg_stop; // Enable stop by stallGuard2 (also available in dcStep mode). Disable to release motor after stop event. 224 | BitField<11> en_softstop; // Enable soft stop upon a stop event (uses the deceleration ramp settings) 225 | }; 226 | 227 | /* Ramp status and switch event status */ 228 | union RAMP_STAT_Register { 229 | uint32_t value; 230 | BitField< 0> status_stop_l; // Reference switch left status (1=active) 231 | BitField< 1> status_stop_r; // Reference switch right status (1=active) 232 | BitField< 2> status_latch_l; // Latch left ready (enable position latching using SWITCH_MODE settings latch_l_active or latch_l_inactive) 233 | BitField< 3> status_latch_r; // Latch right ready (enable position latching using SWITCH_MODE settings latch_r_active or latch_r_inactive) 234 | BitField< 4> event_stop_l; // Signals an active stop left condition due to stop switch. 235 | BitField< 5> event_stop_r; // Signals an active stop right condition due to stop switch. 236 | BitField< 6> event_stop_sg; // Signals an active StallGuard2 stop event. 237 | BitField< 7> event_pos_reached; // Signals that the target position has been reached (position_reached becoming active). 238 | BitField< 8> velocity_reached; // Signals that the target velocity is reached. 239 | BitField< 9> position_reached; // Signals that the target position is reached. 240 | BitField<10> vzero; // Signals that the actual velocity is 0. 241 | BitField<11> t_zerowait_active; // Signals that TZEROWAIT is active after a motor stop. During this time, the motor is in standstill. 242 | BitField<12> second_move; // Signals that the automatic ramp required moving back in the opposite direction 243 | BitField<13> status_sg; // Signals an active stallGuard2 input from the coolStep driver or from the dcStep unit, if enabled. 244 | }; 245 | 246 | /* Encoder configuration and use of N channel */ 247 | union ENCMODE_Register { 248 | uint32_t value; 249 | BitField< 0> pol_A; // Required A polarity for an N channel event (0=neg., 1=pos.) 250 | BitField< 1> pol_B; // Required B polarity for an N channel event (0=neg., 1=pos.) 251 | BitField< 2> pol_N; // Defines active polarity of N (0=low active, 1=high active) 252 | BitField< 3> ignore_AB; // Ignore A and B polarity for N channel event 253 | BitField< 4> clr_cont; // Always latch or latch and clear X_ENC upon an N event 254 | BitField< 5> clr_once; // Latch or latch and clear X_ENC on the next N event following the write access 255 | BitField< 6, 2> sensitivity; // N channel event sensitivity 256 | BitField< 8> clr_enc_x; // Clear encoder counter X_ENC upon N-event 257 | BitField< 9> latch_x_act; // Also latch XACTUAL position together with X_ENC. 258 | BitField<10> enc_sel_decimal; // Encoder prescaler divisor binary mode (0) / decimal mode (1) 259 | }; 260 | 261 | /* Encoder status information */ 262 | union ENC_STATUS_Register { 263 | uint32_t value; 264 | BitField<0> n_event; // N event detected 265 | BitField<1> deviation_warn; //Deviation between X_ACTUAL and X_ENC detected 266 | }; 267 | 268 | /* Chopper and driver configuration */ 269 | union CHOPCONF_Register { 270 | uint32_t value; 271 | BitField< 0, 4> toff; // Off time setting controls duration of slow decay phase. 0 : Driver disabled. 272 | BitField< 4, 3> hstrt_tfd; // chm=0: hysteresis start value HSTRT, chm=1: fast decay time setting bits 0:2 273 | BitField< 7, 4> hend_offset; // chm=0: hysteresis low value HEND, chm=1: sine wave offset 274 | BitField<11> tfd_3; // chm=1: fast decay time setting bit 3 275 | BitField<12> disfdcc; // chm=1: disable current comparator usage for termi- nation of the fast decay cycle 276 | BitField<13> rndtf; // enable random modulation of chopper TOFF time 277 | BitField<14> chm; // Chopper mode (0=standard - spreadCycle ; 1=constant off time with fast decay time) 278 | BitField<15, 2> tbl; // Comparator blank time select. 279 | BitField<17> vsense; // Select resistor voltage sensitivity (0=Low sensitivity ; 1=High sensitivity) 280 | BitField<18> vhighfs; // Enable switching to fullstep when VHIGH is exceeded. 281 | BitField<19> vhighchm; // Enable switching to chm=1 and fd=0 when VHIGH is exceeded 282 | BitField<20, 4> tpfd; // passive fast decay time 283 | BitField<24, 4> mres; // Set microstep resolution 284 | BitField<28> intpol; // Enable interpolation to 256 microsteps with an external motion controller 285 | BitField<29> dedge; // Enable double edge step pulses 286 | BitField<30> diss2g; // Disable short to GND protection 287 | BitField<31> diss2vs; // Disable short to supply protection 288 | }; 289 | 290 | /* coolStep smart current control and stallGuard2 configuration */ 291 | union COOLCONF_Register { 292 | uint32_t value; 293 | BitField< 0, 4> semin; // Minimum stallGuard2 value for smart current control and smart current enable 294 | BitField< 5, 2> seup; // Current increment step width 295 | BitField< 8, 4> semax; // stallGuard2 hysteresis value for smart current control 296 | BitField<13, 2> sedn; // Current decrement step speed 297 | BitField<15> seimin; // Minimum current for smart current control 298 | BitField<16, 7> sgt; // stallGuard2 threshold value 299 | BitField<24> sfilt; // Enable stallGuard2 filter 300 | }; 301 | 302 | /* dcStep automatic commutation configuration register */ 303 | union DCCTRL_Register { 304 | uint32_t value; 305 | BitField< 0, 10> dc_time; // Upper PWM on time limit for commutation 306 | BitField<16, 8> dc_sg; // Max. PWM on time for step loss detection using dcStep stallGuard2 in dcStep mode. 307 | }; 308 | 309 | /* stallGuard2 value and driver error flags */ 310 | union DRV_STATUS_Register { 311 | uint32_t value; 312 | BitField< 0, 9> sg_result; // stallGuard2 result or motor temperature estimation in stand still 313 | BitField<12> s2vsa; // short to supply indicator phase A 314 | BitField<13> s2vsb; // short to supply indicator phase B 315 | BitField<14> stealth; // stealthChop indicator 316 | BitField<15> fsactive; // Full step active indicator 317 | BitField<16, 5> cs_actual; // Actual motor current / smart energy current 318 | BitField<24> stallguard; // stallGuard2 status 319 | BitField<25> ot; // overtemperature flag 320 | BitField<26> otpw; // overtemperature pre- warning flag 321 | BitField<27> s2ga; // short to ground indicator phase A 322 | BitField<28> s2gb; // short to ground indicator phase B 323 | BitField<29> ola; // open load indicator phase A 324 | BitField<30> olb; // open load indicator phase B 325 | BitField<31> stst; // standstill indicator 326 | }; 327 | 328 | /* stealthChop voltage PWM mode chopper configuration */ 329 | union PWMCONF_Register { 330 | uint32_t value; 331 | BitField< 0, 8> pwm_ofs; // User defined PWM amplitude (offset) 332 | BitField< 8, 8> pwm_grad; // User defined PWM amplitude (gradient) 333 | BitField<16, 2> pwm_freq; // PWM frequency selection 334 | BitField<18> pwm_autoscale; // Enable PWM automatic amplitude scaling 335 | BitField<19> pwm_autograd; // PWM automatic gradient adaptation 336 | BitField<20, 2> freewheel; // Stand still option when motor current setting is zero (I_HOLD=0). 337 | BitField<24, 4> pwm_reg; // Regulation loop gradient 338 | BitField<28, 4> pwm_lim; // PWM automatic scale amplitude limit when switching on 339 | }; 340 | 341 | /* Results of stealthChop amplitude regulator */ 342 | union PWM_SCALE_Register { 343 | uint32_t value; 344 | BitField< 0, 8> pwm_scale_sum; // Actual PWM duty cycle 345 | BitField<16, 9> pwm_scale_auto; // Result of the automatic amplitude regulation based on current measurement. 346 | }; 347 | 348 | /* stealthChop automatically generated values read out */ 349 | union PWM_AUTO_Register { 350 | uint32_t value; 351 | BitField< 0, 8> pwm_ofs_auto; // Automatically determined offset value 352 | BitField<16, 8> pwm_grad_auto; // Automatically determined gradient value 353 | }; 354 | 355 | /* Register field values */ 356 | enum RAMPMODE_Values { 357 | POSITIONING_MODE = 0x00, // using all A, D and V parameters 358 | VELOCITY_MODE_POS = 0x01, // positive VMAX, using AMAX acceleration 359 | VELOCITY_MODE_NEG = 0x02, // negative VMAX, using AMAX acceleration 360 | HOLD_MODE = 0x03 // velocity remains unchanged, unless stop event occurs 361 | }; 362 | 363 | enum PWMCONF_freewheel_Values { 364 | FREEWHEEL_NORMAL = 0x00, // Normal operation 365 | FREEWHEEL_ENABLED = 0x01, // Freewheeling 366 | FREEWHEEL_SHORT_LS = 0x02, // Coil shorted using LS drivers 367 | FREEWHEEL_SHORT_HS = 0x03 // Coil shorted using HS drivers 368 | }; 369 | 370 | enum ENCMODE_sensitivity_Values { 371 | ENCODER_N_NO_EDGE = 0x00, // N channel active while the N event is valid 372 | ENCODER_N_RISING_EDGE = 0x01, // N channel active when the N event is activated 373 | ENCODER_N_FALLING_EDGE = 0x02, // N channel active when the N event is de-activated 374 | ENCODER_N_BOTH_EDGES = 0x03 // N channel active on N event activation and de-activation 375 | }; 376 | } 377 | 378 | #endif // TMC5160_REGISTERS_H 379 | --------------------------------------------------------------------------------