Quantcast
Viewing all articles
Browse latest Browse all 66

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! 

Image may be NSFW.
Clik here to view.
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_P			4952
#define TORQUE_FLUX_I		   10939
#define VELOCITY_P				2200
#define VELOCITY_I				2000
#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_FACTOR			1024
//end mydefines


void 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);  //max
	tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767); 			    //max
	tmc4671_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_abn
	tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN);
	tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, TMC4671_POSITION_PHI_M_ABN);	//phi_m_abn
	tmc4671_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 guide
	tmc4671_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 VM
uint32_t current_VM()
{
	uint32_t VM;

	VM = *HAL.ADCs->VM;              // read ADC value for motor supply VM
	VM = (VM*VM_FACTOR)/ADC_VM_RES;  // calculate voltage from ADC value

	return VM;
}
//End Check VM

void init_Basics(uint8_t motor)
{
	//Configure basic registers like motor type, adc-offset

	tmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0x00020032); //x0002 - Stepper, x0032 = 50 polepairs
	tmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0x00000000);
	tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0x00000F9F); 			  //Maxcount of PWM-counter
	tmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0x00001919); 		  //Break before make times
	tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0x00000007);			  //turn on PWM

	//Configure ADC -- Values were taken from the ide after the set offset option was used
	tmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0x18000100); 			  // configure ADC-Inputs
	tmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0x00100010);	  // configure ADCs
	tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0x20000000);			  // turn on  clock-signal for group-A ADCs
	tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0x00000000);			  // turn off clock-signal for group-B ADCs
	tmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0x014E014E);	  // Constants for filter
	tmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0x01005D80); 	  // Fill in the offsets determined in the IDE
	tmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0x01005CEF); 	  // Fill in the offsets determined in the IDE

	// Open loop settings
	tmc4671_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 selection
	tmc4671_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 Limits
	tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0x7FFF); //max
	tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767); 			    //max
	tmc4671_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 0
	wait(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 turns
	wait(1500);
	/* After slide reached one end of the setup the absolute position is stored in memory
		For 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 position
	wait(1000);

	// Assumed an unreachable position so the slide reaches the other side of the setup
	tmc4671_setAbsolutTargetPosition(motor, -500000); // ~ -7.5 motor turns
	wait(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 zero
	tmc4671_writeRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M_OFFSET, BIT_16_TO_31, 0);
	// select phi_e_ext
	tmc4671_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" angle
	tmc4671_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 it
	tmc4671_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-position
	tmc4671_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 registers
		reset_Basics(motor);
		//set TMC4671 registers with basic data like motortype, number of polepairs and so on
		init_Basics(motor);

		//initialize the software ramp
		init_softwareRamp(linearRamp);
		//configure ABN
		configure_ABN(motor, startVoltage);

		//Enter position-Mode
		init_PosMode(motor);

		//configure the end positions and store them in *lower_End and *upper_End
		find_targetPositions(motor, lower_End, upper_End);

		*initState = STATE_RUN_1;
		//set the first target position using the software ramp
		tmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR));
		break;
	case STATE_RUN_1:

		//drive from one end to the other: positive direction
		tmc_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 direction
		if(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 direction
		tmc_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 direction
		if(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 key
		0xFF, 0xFF, 0xFF, 0xFF,                          //Flash protection (FPPROT)
		0x7E,                                            //Flash security   (FSEC)
		0xF9,                                            //Flash option     (FOPT)
		0xFF,                                            //reserved
		0xFF                                             //reserved
	};

	struct BootloaderConfig {
		uint32_t BLMagic;
		uint32_t drvEnableResetValue;
	};

	// This struct gets placed at a specific address by the linker
	struct 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 together
	HAL.IOs->config->toOutput(&amp;HAL.IOs->pins->ID_CLK);
	HAL.IOs->config->toInput(&amp;HAL.IOs->pins->ID_CH0);

	HAL.IOs->config->setHigh(&amp;HAL.IOs->pins->ID_CLK);
	if(!HAL.IOs->config->isHigh(&amp;HAL.IOs->pins->ID_CH0))
		return;

	HAL.IOs->config->setLow(&amp;HAL.IOs->pins->ID_CLK);
	if(HAL.IOs->config->isHigh(&amp;HAL.IOs->pins->ID_CH0))
		return;

	HAL.IOs->config->toOutput(&amp;HAL.IOs->pins->ID_CH0);
	HAL.IOs->config->toInput(&amp;HAL.IOs->pins->ID_CLK);

	HAL.IOs->config->setHigh(&amp;HAL.IOs->pins->ID_CH0);
	if(!HAL.IOs->config->isHigh(&amp;HAL.IOs->pins->ID_CLK))
		return;

	HAL.IOs->config->setLow(&amp;HAL.IOs->pins->ID_CH0);
	if(HAL.IOs->config->isHigh(&amp;HAL.IOs->pins->ID_CLK))
		return;

	// not returned, this means pins are tied together
	tmcl_boot();
}


/* Call all standard initialization routines. */
static void init()
{
#if defined(Landungsbruecke)
	// Default value: Driver enable gets set high by the bootloader
	BLConfig.drvEnableResetValue = 1;
#endif

	HAL.init();                  // Initialize Hardware Abstraction Layer
	IDDetection_init();          // Initialize board detection
	tmcl_init();                 // Initialize TMCL communication

	tmcdriver_init();            // Initialize dummy driver board --> preset EvalBoards.ch2
	tmcmotioncontroller_init();  // Initialize dummy motion controller board  --> preset EvalBoards.ch1

	VitalSignsMonitor.busy = 1;  // Put state to busy
	Evalboards.driverEnable = DRIVER_ENABLE;

	Evalboards.ch1.id = 0;       // preset id for driver board to 0 --> error/not found
	Evalboards.ch2.id = 0;       // preset id for driver board to 0 --> error/not found

	// We disable the drivers before configurating anything
	HAL.IOs->config->toOutput(&amp;HAL.IOs->pins->DIO0);
	HAL.IOs->config->setHigh(&amp;HAL.IOs->pins->DIO0);

	IdAssignmentTypeDef ids = { 0 };
	IDDetection_initialScan(&amp;ids);  // start initial board detection
	IDDetection_initialScan(&amp;ids);  // start second time, first time not 100% reliable, not sure why - too fast after startup?
	if(!ids.ch1.id &amp;&amp; !ids.ch2.id)
	{
		shallForceBoot();           // only checking to force jump into bootloader if there are no boards attached
		HAL.IOs->config->toOutput(&amp;HAL.IOs->pins->ID_CLK);
		HAL.IOs->config->toInput(&amp;HAL.IOs->pins->ID_CH0);
	}
	Board_assign(&amp;ids);             // assign boards with detected id

	VitalSignsMonitor.busy 	= 0;    // not busy any more!
}

/* main function */
int main(void)
{
	//initialise some pointers
	//state pointer for fsm
	uint8_t   init_State = STATE_START_INIT;
	uint8_t  *initState;
	initState = &amp;init_State;

	//store one end of the track
	int32_t   upper_end = 0;
	int32_t  *upper_End;
	upper_End= &amp;upper_end;

	//store the other end
	int32_t   lower_end = 0;
	int32_t  *lower_End;
	lower_End= &amp;lower_end;

	//convenience
	uint8_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 loop
	while(1)
	{
		// Check all parameters and life signs and mark errors
		vitalsignsmonitor_checkVitalSigns();
		// Perodic jobs of Motion controller/Driver boards
		Evalboards.ch1.periodicJob(systick_getTick());

		Evalboards.ch2.periodicJob(systick_getTick());
		// Process TMCL communication
		tmcl_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 reset
			reset_Basics(motor);							 // Set the registers needed in this example to a defined default state
			continue;
		}

		if(block == 0)
		{
			tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0);	//Turn of PWM-Register after SPI was initialized
			reset_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 application example and stay tuned for my next TMC4671 blogpost.


Viewing all articles
Browse latest Browse all 66

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>