Driving a linear stage with the TMC4671 – Firmware Adaptation

Driving a linear stage with the TMC4671 – Firmware Adaptation

In our last blog post, we were able to determine stable P-I-parameters for our cascaded controller loop. With that knowledge in mind I want to show you how I created a small example program which I will include in the firmware of the Landungsbrücke. 

What this code basically does is initializing the TMC4671 for stepper motor control. This is done in init_Basics(…) by writing several configuration-registers whereas reset_Basics(…) resets all registers used in this work to a defined state of 0. This replaces the first steps in the configuration wizard of our IDE where we used the default buttons. 

The configure_ABN(…)-function will set the ABN-count to 0 after PHI_E aligned the rotor to 0. For this to happen we need to apply a voltage to the flux as well. 

init_PosMode(…) sets the correct registers for the TMC4671 to enter position mode.  

Using tmc4671_setAbsolutTargetPosition(…) sets the target position of the controller to the desired value, making the motor turn until the target position is reached. Here the absolute position will always refer to our zero position configured in the configure_ABN(…) function.  

Side note: If you want movement relative to your actual position you can use the function tmc4671_setRelativeTargetPosition(…). 

In order to find the minimum and maximum positions of my setup, I select two target positions in  find_targetPositions(…) that are impossible to reach. For me, these are 500000 and –500000. The controller waits a certain amount of time to be sure that it reached an end position. Then it will save the actual position with the function tmc4671_getActualPosition(…).  We do that for the other direction as well. The values of both endings can now be used for new target positions which can be set by using tmc4671_setAbsolutTargetPosition(…) again.  

I can also use linear ramping for acceleration and deceleration while driving. I use the function tmc_ramp_linear_compute(…) and a struct of type TMC_LinearRamp for this (you can find more information in the API in the files LinearRamp1.h and LinearRamp1.c). This creates a ramp with the information of our track in software. I also set some of the parameters of the struct using the corresponding functions from the aforementioned files. I can set the target position for my ramp using tmc_ramp_linear_set_targetPosition(…) in the struct. After that, I use the calculated ramp position in tmc4671_setAbsolutTargetPosition(calculated_Ramp). This will be looped until the actual target position is reached. 

What does ramping do? Instead of setting an absolute target position to be reached with one instruction, we divide our track in multiple sub-target positions. In each iteration of the loop, we increase the target position and the controller will adjust to the new target. However, the velocity of the Landungsbrücke increasing the target position is higher than the adaption of our controller, thus increasing the difference between target position and actual motor position over time.  This leads to the motor accelerating as the offset between actual and target position increases. 

Decelerating works the other way around. The Landungsbrücke will decrease the velocity in which the target position is set and the controller will slowly catch up to it until it reaches the end position. 

In order to increase the speed of the motor, I multiply the actual ramp position by a factor of 1024. This must be compensated by dividing the target position of the software ramp by 1024. With this we scale the ramp to our application and increase the acceleration and velocity of the motor. 

Why do I want to use a (software) ramp? Basically, it gives us a more stable way to reach our target position as we have a determined way of accelerating and decelerating and it reduces the possibility of overshooting the target significantly. 

The Landungsbrücke alternates these target positions in the TMC4671 register so the motor will be alternating the position of the slide. 

With the finished code I generate a hex-file for the bootloader of the Landungsbrücke and update it using the TMCL-IDE.  

I hope you can utilize some of the shown examples in order for you to transform digital information into physical motion for your projects! Thank you for keeping up with these blog posts and keep your eyes open for more to come! 

Figure 1: Flashing the firmware

Code and explanation

#include "boards/Board.h"#include "hal/derivative.h"#include "hal/HAL.h"#include "tmc/IdDetection.h"#include "tmc/TMCL.h"#include "tmc/VitalSignsMonitor.h"#include "tmc/BoardAssignment.h"#include "tmc/ramp/LinearRamp1.h"#include "TMC-API/tmc/ic/TMC4671/TMC4671.h"#include "TMC-API/tmc/ic/TMC4671/TMC4671_Register.h" //As I use the names of its registers this needs to be included//mydefines#define STATE_START_INIT       1#define STATE_RUN_1   2#define STATE_RUN_2   3#define UD_EXT   3202//Torque-Flux Limits 15000#define TORQUE_FLUX_LIMIT   10000//Velocity Limit 10000#define VELOCITY_LIMIT   10000//P-I-defines#define TORQUE_FLUX_P4952#define TORQUE_FLUX_I   10939#define VELOCITY_P2200#define VELOCITY_I2000#define POSITION_P  45#define POSITION_I   0//for checking motor supply voltage#define VM_FACTOR              713#define ADC_VM_RES    65535 //Landungsbruecke define -- change for Startrampe//Software-Ramp defines#define STEPDIR_FREQUENCY         (1 << 17)#define STEPDIR_MAX_VELOCITY      (1 << 20)#define STEPDIR_MAX_ACCELERATION  2147418111#define STEPDIR_DEFAULT_ACCELERATION 100000#define STEPDIR_DEFAULT_VELOCITY STEPDIR_MAX_VELOCITY#define RAMP_SPEED_FACTOR1024//end mydefinesvoid init_PosMode(uint8_t motor){//Set registers needed for position mode//tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN);tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 32767);  //maxtmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767);     //maxtmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, TORQUE_FLUX_LIMIT);tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 2147483647);tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, VELOCITY_LIMIT);tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0x80000001);tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0x7FFFFFFF);tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000003);  //phi_e_abntmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN);tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, TMC4671_POSITION_PHI_M_ABN);//phi_m_abntmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0x00000003);  //position mode//The following gain-values were determined using the USB-2-RTMI and following the PI-tuning guidetmc4671_setTorqueFluxPI(motor, TORQUE_FLUX_P, TORQUE_FLUX_I); //(motor, P, I)tmc4671_setVelocityPI(motor, VELOCITY_P, VELOCITY_I);tmc4671_setPositionPI(motor, POSITION_P, POSITION_I);}//Check VMuint32_t current_VM(){uint32_t VM;VM = *HAL.ADCs->VM;              // read ADC value for motor supply VMVM = (VM*VM_FACTOR)/ADC_VM_RES;  // calculate voltage from ADC valuereturn VM;}//End Check VMvoid init_Basics(uint8_t motor){//Configure basic registers like motor type, adc-offsettmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0x00020032); //x0002 - Stepper, x0032 = 50 polepairstmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0x00000000);tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0x00000F9F);   //Maxcount of PWM-countertmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0x00001919);   //Break before make timestmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0x00000007);  //turn on PWM//Configure ADC -- Values were taken from the ide after the set offset option was usedtmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0x18000100);   // configure ADC-Inputstmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0x00100010);  // configure ADCstmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0x20000000);  // turn on  clock-signal for group-A ADCstmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0x00000000);  // turn off clock-signal for group-B ADCstmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0x014E014E);  // Constants for filtertmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0x01005D80);   // Fill in the offsets determined in the IDEtmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0x01005CEF);   // Fill in the offsets determined in the IDE// Open loop settingstmc4671_writeInt(motor, TMC4671_OPENLOOP_MODE, 0x00000000);tmc4671_writeInt(motor, TMC4671_OPENLOOP_ACCELERATION, 0x0000003C);tmc4671_writeInt(motor, TMC4671_OPENLOOP_VELOCITY_TARGET, 0x00000000);tmc4671_writeInt(motor, TMC4671_OPENLOOP_PHI, 0x00000000);// Feedback selectiontmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000000);tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0x00000008);//tmc4671_writeInt(motor, TMC4671_UQ_UD_EXT, 0x00000F9F);tmc4671_writeInt(motor, TMC4671_ABN_DECODER_PPR, 0x00009C40);//Set Limitstmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0x7FFF); //maxtmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767);     //maxtmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, TORQUE_FLUX_LIMIT);tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 2147483647);tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, VELOCITY_LIMIT);tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0x80000001);tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0x7FFFFFFF);}void reset_Basics(uint8_t motor){//reset registers of TMC4671 that will later be used to 0wait(1500);tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0);tmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0);tmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0);tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0);tmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0);tmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0);tmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0);tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0);tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0);tmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0);tmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0);tmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0);tmc4671_writeInt(motor, TMC4671_OPENLOOP_MODE, 0);tmc4671_writeInt(motor, TMC4671_OPENLOOP_ACCELERATION, 0);tmc4671_writeInt(motor, TMC4671_OPENLOOP_VELOCITY_TARGET, 0);tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0);tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0);tmc4671_writeInt(motor, TMC4671_UQ_UD_EXT, 0);tmc4671_writeInt(motor, TMC4671_ABN_DECODER_PPR, 0);tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0);tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 0);tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, 0);tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 0);tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, 0);tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0);tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0);tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0);tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, 0);tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, 0);tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0);tmc4671_writeInt(motor, TMC4671_PID_POSITION_TARGET, 0);tmc4671_writeInt(motor, TMC4671_PID_POSITION_ACTUAL, 0);tmc4671_setTorqueFluxPI(motor, 0, 0); //(motor, P, I)tmc4671_setVelocityPI(motor, 0, 0); // (motor, P, I)tmc4671_setPositionPI(motor, 0, 0); // (motor, P, I)}void init_softwareRamp(TMC_LinearRamp *linearRamp){tmc_ramp_linear_init(linearRamp);tmc_ramp_linear_set_mode(linearRamp, TMC_RAMP_LINEAR_MODE_POSITION);tmc_ramp_linear_set_maxVelocity(linearRamp, STEPDIR_DEFAULT_VELOCITY);tmc_ramp_linear_set_acceleration(linearRamp, STEPDIR_DEFAULT_ACCELERATION);}void find_targetPositions(uint8_t motor, int32_t *lower_End, int32_t*upper_End){tmc4671_setAbsolutTargetPosition(motor, 500000); // ~ 7.5 motor turnswait(1500);/* After slide reached one end of the setup the absolute position is stored in memoryFor convenience I reduce the value of the target position to be saved a little, *  as driving into the endposition with full force is annoyingly loud :) *  if this is your goal just remove the +/- 1000 */*lower_End = (tmc4671_getActualPosition(motor)-1000);tmc4671_setAbsolutTargetPosition(motor, 0); //return to the starting positionwait(1000);// Assumed an unreachable position so the slide reaches the other side of the setuptmc4671_setAbsolutTargetPosition(motor, -500000); // ~ -7.5 motor turnswait(1500);// After slide reached the other end of the setup the absolute position is stored in memory*upper_End = (tmc4671_getActualPosition(motor)+1000);tmc4671_setAbsolutTargetPosition(motor, 0);wait(1500);}void configure_ABN(uint8_t motor, uint16_t startVoltage){// set ABN_DECODER_PHI_E_OFFSET to zerotmc4671_writeRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M_OFFSET, BIT_16_TO_31, 0);// select phi_e_exttmc4671_writeRegister16BitValue(motor, TMC4671_PHI_E_SELECTION, BIT_0_TO_15, 1);// set an initialization voltage on UD_EXT (to the flux, not the torque!)tmc4671_writeRegister16BitValue(motor, TMC4671_UQ_UD_EXT, BIT_16_TO_31, 0);tmc4671_writeRegister16BitValue(motor, TMC4671_UQ_UD_EXT, BIT_0_TO_15, startVoltage);// set the "zero" angletmc4671_writeRegister16BitValue(motor, TMC4671_PHI_E_EXT, BIT_0_TO_15, 0);tmc4671_writeInt(motor, TMC4671_ABN_DECODER_MODE, 0x00001000);//Set PHI_E to 0° angle so the rotor alignes itself with ittmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000000);wait(4000);tmc4671_readRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M, BIT_16_TO_31);//After the rotor has aligned this will set the ABN_DECODER_COUNT to 0 so there is a defined 0-positiontmc4671_writeInt(motor, TMC4671_ABN_DECODER_COUNT, 0);}void tmc4671setPositionModeAndRun(uint8_t motor, uint8_t *initState, uint16_t startVoltage, int32_t *upper_End, int32_t *lower_End, TMC_LinearRamp *linearRamp){/*State Machine with state-pointer *initState and 3 states * STATE_START_INIT: Sets TMC4671-registers for my application and configures the ABN-Encoder *  Drives to the ends of the track to get the actual min/max position values *  Drives to the first end position * * STATE_RUN_1: Drives to one end and changes direction when the *lower_End is reached * * STATE_RUN_2: Drives to the other end and changes direction when *upper_End is reached */switch (*initState){case STATE_START_INIT://reset TMC4671 registersreset_Basics(motor);//set TMC4671 registers with basic data like motortype, number of polepairs and so oninit_Basics(motor);//initialize the software rampinit_softwareRamp(linearRamp);//configure ABNconfigure_ABN(motor, startVoltage);//Enter position-Modeinit_PosMode(motor);//configure the end positions and store them in *lower_End and *upper_Endfind_targetPositions(motor, lower_End, upper_End);*initState = STATE_RUN_1;//set the first target position using the software ramptmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR));break;case STATE_RUN_1://drive from one end to the other: positive directiontmc_ramp_linear_compute(linearRamp);tmc4671_setAbsolutTargetPosition(motor, (RAMP_SPEED_FACTOR*tmc_ramp_linear_get_rampPosition(linearRamp)));//When the target position is reached we will change directionif(tmc4671_getActualPosition(motor) >= (*lower_End-RAMP_SPEED_FACTOR)){wait(50);*initState = STATE_RUN_2;tmc_ramp_linear_set_targetPosition(linearRamp, (*upper_End/RAMP_SPEED_FACTOR));break;}*initState = STATE_RUN_1;break;case STATE_RUN_2://drive from one end to the other: negative directiontmc_ramp_linear_compute(linearRamp);tmc4671_setAbsolutTargetPosition(motor, (RAMP_SPEED_FACTOR*tmc_ramp_linear_get_rampPosition(linearRamp)));//When the target position is reached we will change directionif(tmc4671_getActualPosition(motor) <= (*upper_End + RAMP_SPEED_FACTOR)){wait(50);*initState = STATE_RUN_1;tmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR));break;}*initState = STATE_RUN_2;break;default:*initState = 0;break;}}const char *VersionString = MODULE_ID"V308"; // module id and version of the firmware shown in the TMCL-IDE/* Keep as is! These lines are important for the update functionality. */#if defined(Landungsbruecke)const uint8_t Protection[] __attribute__ ((section(".cfmconfig")))={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  //Backdoor key0xFF, 0xFF, 0xFF, 0xFF,                          //Flash protection (FPPROT)0x7E,                                            //Flash security   (FSEC)0xF9,                                            //Flash option     (FOPT)0xFF,                                            //reserved0xFF                                             //reserved};struct BootloaderConfig {uint32_t BLMagic;uint32_t drvEnableResetValue;};// This struct gets placed at a specific address by the linkerstruct BootloaderConfig __attribute__ ((section(".bldata"))) BLConfig;#endif/* Check if jumping into bootloader is forced                                           *//*                                                                                      *//* In order to jump to bootloader e.g. because of an accidental infinite loop           *//* in a modified firmware you may short ID_CLK and ID_CH0 pins on start up.             *//* This will force the entrance into bootloader mode and allow to replace bad firmware. */void shallForceBoot(){// toggle each pin and see if you can read the state on the other// leave if not, because this means that the pins are not tied togetherHAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK);HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CH0);HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CLK);if(!HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CH0))return;HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CLK);if(HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CH0))return;HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CH0);HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CLK);HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CH0);if(!HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CLK))return;HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CH0);if(HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CLK))return;// not returned, this means pins are tied togethertmcl_boot();}/* Call all standard initialization routines. */static void init(){#if defined(Landungsbruecke)// Default value: Driver enable gets set high by the bootloaderBLConfig.drvEnableResetValue = 1;#endifHAL.init();                  // Initialize Hardware Abstraction LayerIDDetection_init();          // Initialize board detectiontmcl_init();                 // Initialize TMCL communicationtmcdriver_init();            // Initialize dummy driver board --> preset EvalBoards.ch2tmcmotioncontroller_init();  // Initialize dummy motion controller board  --> preset EvalBoards.ch1VitalSignsMonitor.busy = 1;  // Put state to busyEvalboards.driverEnable = DRIVER_ENABLE;Evalboards.ch1.id = 0;       // preset id for driver board to 0 --> error/not foundEvalboards.ch2.id = 0;       // preset id for driver board to 0 --> error/not found// We disable the drivers before configurating anythingHAL.IOs->config->toOutput(&HAL.IOs->pins->DIO0);HAL.IOs->config->setHigh(&HAL.IOs->pins->DIO0);IdAssignmentTypeDef ids = { 0 };IDDetection_initialScan(&ids);  // start initial board detectionIDDetection_initialScan(&ids);  // start second time, first time not 100% reliable, not sure why - too fast after startup?if(!ids.ch1.id && !ids.ch2.id){shallForceBoot();           // only checking to force jump into bootloader if there are no boards attachedHAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK);HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CH0);}Board_assign(&ids);             // assign boards with detected idVitalSignsMonitor.busy = 0;    // not busy any more!}/* main function */int main(void){//initialise some pointers//state pointer for fsmuint8_t   init_State = STATE_START_INIT;uint8_t  *initState;initState = &init_State;//store one end of the trackint32_t   upper_end = 0;int32_t  *upper_End;upper_End= &upper_end;//store the other endint32_t   lower_end = 0;int32_t  *lower_End;lower_End= &lower_end;//convenienceuint8_t motor = 0;//"flipflop"uint8_t block = 0;//struct used for the software ramp - see \TMC-API\tmc\ramp\LinearRamp1.h for typedef etc.TMC_LinearRamp adressRamp;TMC_LinearRamp *aRamp;aRamp = &adressRamp;init();// Main loopwhile(1){// Check all parameters and life signs and mark errorsvitalsignsmonitor_checkVitalSigns();// Perodic jobs of Motion controller/Driver boardsEvalboards.ch1.periodicJob(systick_getTick());Evalboards.ch2.periodicJob(systick_getTick());// Process TMCL communicationtmcl_process();if (Evalboards.ch1.id != ID_TMC4671)continue;if(current_VM()<20){tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0); // Turn off PWM after unexpected resetreset_Basics(motor); // Set the registers needed in this example to a defined default statecontinue;}if(block == 0){tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0);//Turn of PWM-Register after SPI was initializedreset_Basics(motor);block = 1;}tmc4671setPositionModeAndRun(motor, initState, UD_EXT, lower_End, upper_End, aRamp);}return 0;}

With this code my drive is initialized, the encoder initialization is performed and homing on both end stops is performed. Afterwards, a back and forth movement is implemented.

I can upload the firmware to the Landungsbrücke with the TMCL-IDE and it starts automatically after power-up. I can monitor the system behavior over the RTMI.

You can copy the code above, or get the main.c file here.

That’s it for today. I hope you like the applicationexample and stay tuned for my next TMC4671 blogpost.