Limit switch setup options

Moderators: TomKerekes, dynomotion

Post Reply
jpbarad7
Posts: 15
Joined: Wed Dec 07, 2022 6:25 pm

Limit switch setup options

Post by jpbarad7 » Mon Nov 11, 2024 10:41 pm

Tom,

I have my (+) and (-) limit switches on each axis wired in series and connected to opto-inputs on KFLOP.

The limit switch option selected is 'Stop movement'

I am using Mach3 and an iMach pendant to move the axes.

When the X axis is driven into the (+) motion limit switch, the axis stops immediately and the KMotion console reports 'Pos Limit Stop Axis:0'. If I jog the pendant rotary encoder dial in the (-) direction, the axis does not move, but the console reports 'Neg Limit Stop Axis:0'.

Reaching the limit switches on the other axes results in the same action.

The Manual says that pressing the 'Feed Hold' button should allow motion to resume however, pressing the 'Feed hold' button does nothing in Mach3, KMotion, or KMotionCNC (which I also used to drive the axis into the same limit switch).

All axes remain enabled, eventhough they don't move.

Any idea what might be going on?

Am I supposed to configure Mach3's input signals to align with KFLOP's limit switch connections?

Initialization code posted below.

Thanks

Jim

Code: Select all

#include "KMotionDef.h"

// Defines KFLOP channels 0-4 as axes X-C
// Disables and Zeros all axes
// Defines axis parameters
// Enables all axes and sets positions to Zero
// Sets them as an XYZAC coordinate system for GCode
// Watchenable loop turns servos ON/OFF when axes are ENABLED/DISABLED

void ServiceWatchdogStatus(void);
void WatchdogTripped(void);
void WatchdogOK(void);

#define X 0
#define Y 1
#define Z 2					// Quill
#define C 3					// Knee
#define A 4					// Rotary

#define WATCHDOG_DELAY 5.0 	// Script to disable drives if/when Mach3 program is closed

int Mach3Active = 1;
int DrivePower = 0;
int CycledOFF = 0;

int main() 
{
	DisableAxis(X);
	Zero(X);

	DisableAxis(Y);
	Zero(Y);

	DisableAxis(Z);
	Zero(Z);

	DisableAxis(C);
	Zero(C);

	DisableAxis(A);
	Zero(A);	

FPGA(STEP_PULSE_LENGTH_ADD)=32 + 0x40 + 0x80;	

	ch0->InputMode=ENCODER_MODE;
	ch0->OutputMode=CL_STEP_DIR_MODE;
	ch0->Vel=200000;
	ch0->Accel=1e+06;
	ch0->Jerk=1e+07;
	ch0->P=0;
	ch0->I=0.002;
	ch0->D=0;
	ch0->FFAccel=0;
	ch0->FFVel=0;
	ch0->MaxI=1e+06;
	ch0->MaxErr=1e+09;
	ch0->MaxOutput=1e+06;
	ch0->DeadBandGain=1;
	ch0->DeadBandRange=0;
	ch0->InputChan0=0;
	ch0->InputChan1=0;
	ch0->OutputChan0=56;
	ch0->OutputChan1=0;
	ch0->MasterAxis=-1;
	ch0->LimitSwitchOptions=0x12f;
	ch0->LimitSwitchNegBit=136;
	ch0->LimitSwitchPosBit=136;
	ch0->SoftLimitPos=1e+09;
	ch0->SoftLimitNeg=-1e+09;
	ch0->InputGain0=-46;
	ch0->InputGain1=1;
	ch0->InputOffset0=0;
	ch0->InputOffset1=0;
	ch0->OutputGain=-1;
	ch0->OutputOffset=0;
	ch0->SlaveGain=1;
	ch0->BacklashMode=BACKLASH_OFF;
	ch0->BacklashAmount=0;
	ch0->BacklashRate=0;
	ch0->invDistPerCycle=1;
	ch0->Lead=0;
	ch0->MaxFollowingError=5000;
	ch0->StepperAmplitude=20;

	ch0->iir[0].B0=1;
	ch0->iir[0].B1=0;
	ch0->iir[0].B2=0;
	ch0->iir[0].A1=0;
	ch0->iir[0].A2=0;

	ch0->iir[1].B0=1;
	ch0->iir[1].B1=0;
	ch0->iir[1].B2=0;
	ch0->iir[1].A1=0;
	ch0->iir[1].A2=0;

	ch0->iir[2].B0=0.00075647;
	ch0->iir[2].B1=0.00151294;
	ch0->iir[2].B2=0.00075647;
	ch0->iir[2].A1=1.88998;
	ch0->iir[2].A2=-0.89301;
	


	ch1->InputMode=ENCODER_MODE;
	ch1->OutputMode=CL_STEP_DIR_MODE;
	ch1->Vel=200000;
	ch1->Accel=1e+06;
	ch1->Jerk=1e+07;
	ch1->P=0;
	ch1->I=0.001;
	ch1->D=0;
	ch1->FFAccel=0;
	ch1->FFVel=0;
	ch1->MaxI=1e+06;
	ch1->MaxErr=1e+09;
	ch1->MaxOutput=1e+06;
	ch1->DeadBandGain=1;
	ch1->DeadBandRange=0;
	ch1->InputChan0=1;
	ch1->InputChan1=0;
	ch1->OutputChan0=57;
	ch1->OutputChan1=0;
	ch1->MasterAxis=-1;
	ch1->LimitSwitchOptions=0x12f;
	ch1->LimitSwitchNegBit=137;
	ch1->LimitSwitchPosBit=137;
	ch1->SoftLimitPos=1e+09;
	ch1->SoftLimitNeg=-1e+09;
	ch1->InputGain0=46;
	ch1->InputGain1=1;
	ch1->InputOffset0=0;
	ch1->InputOffset1=0;
	ch1->OutputGain=-1;
	ch1->OutputOffset=0;
	ch1->SlaveGain=1;
	ch1->BacklashMode=BACKLASH_OFF;
	ch1->BacklashAmount=0;
	ch1->BacklashRate=0;
	ch1->invDistPerCycle=1;
	ch1->Lead=0;
	ch1->MaxFollowingError=2500;
	ch1->StepperAmplitude=20;

	ch1->iir[0].B0=1;
	ch1->iir[0].B1=0;
	ch1->iir[0].B2=0;
	ch1->iir[0].A1=0;
	ch1->iir[0].A2=0;

	ch1->iir[1].B0=1;
	ch1->iir[1].B1=0;
	ch1->iir[1].B2=0;
	ch1->iir[1].A1=0;
	ch1->iir[1].A2=0;

	ch1->iir[2].B0=0.779671;
	ch1->iir[2].B1=-1.55685;
	ch1->iir[2].B2=0.779671;
	ch1->iir[2].A1=1.55685;
	ch1->iir[2].A2=-0.559341;



	ch2->InputMode=ENCODER_MODE;
	ch2->OutputMode=CL_STEP_DIR_MODE;
	ch2->Vel=200000;
	ch2->Accel=1e+06;
	ch2->Jerk=1e+06;
	ch2->P=0;
	ch2->I=0.006;
	ch2->D=0;
	ch2->FFAccel=0;
	ch2->FFVel=0;
	ch2->MaxI=1e+06;
	ch2->MaxErr=1e+09;
	ch2->MaxOutput=1e+06;
	ch2->DeadBandGain=1;
	ch2->DeadBandRange=0;
	ch2->InputChan0=2;
	ch2->InputChan1=0;
	ch2->OutputChan0=58;
	ch2->OutputChan1=0;
	ch2->MasterAxis=-1;
	ch2->LimitSwitchOptions=0x12f;
	ch2->LimitSwitchNegBit=138;
	ch2->LimitSwitchPosBit=138;
	ch2->SoftLimitPos=1e+09;
	ch2->SoftLimitNeg=-1e+09;
	ch2->InputGain0=-35.45;
	ch2->InputGain1=1;
	ch2->InputOffset0=0;
	ch2->InputOffset1=0;
	ch2->OutputGain=-1;
	ch2->OutputOffset=0;
	ch2->SlaveGain=1;
	ch2->BacklashMode=BACKLASH_OFF;
	ch2->BacklashAmount=0;
	ch2->BacklashRate=0;
	ch2->invDistPerCycle=1;
	ch2->Lead=0;
	ch2->MaxFollowingError=2000;
	ch2->StepperAmplitude=20;

	ch2->iir[0].B0=1;
	ch2->iir[0].B1=0;
	ch2->iir[0].B2=0;
	ch2->iir[0].A1=0;
	ch2->iir[0].A2=0;

	ch2->iir[1].B0=1;
	ch2->iir[1].B1=0;
	ch2->iir[1].B2=0;
	ch2->iir[1].A1=0;
	ch2->iir[1].A2=0;

	ch2->iir[2].B0=0.000768809;
	ch2->iir[2].B1=0.00153762;
	ch2->iir[2].B2=0.000768809;
	ch2->iir[2].A1=1.92081;
	ch2->iir[2].A2=-0.923885;
	


	ch3->InputMode=ENCODER_MODE;
	ch3->OutputMode=CL_STEP_DIR_MODE;
	ch3->Vel=100000;
	ch3->Accel=75000;
	ch3->Jerk=1e+06;
	ch3->P=0;
	ch3->I=0.001;
	ch3->D=0;
	ch3->FFAccel=0;
	ch3->FFVel=0;
	ch3->MaxI=1e+06;
	ch3->MaxErr=1e+09;
	ch3->MaxOutput=1e+06;
	ch3->DeadBandGain=1;
	ch3->DeadBandRange=0;
	ch3->InputChan0=3;
	ch3->InputChan1=0;
	ch3->OutputChan0=59;
	ch3->OutputChan1=0;
	ch3->MasterAxis=-1;
	ch3->LimitSwitchOptions=0x12f;
	ch3->LimitSwitchNegBit=139;
	ch3->LimitSwitchPosBit=139;
	ch3->SoftLimitPos=1e+09;
	ch3->SoftLimitNeg=-1e+09;
	ch3->InputGain0=-85.3;
	ch3->InputGain1=1;
	ch3->InputOffset0=0;
	ch3->InputOffset1=0;
	ch3->OutputGain=-1;
	ch3->OutputOffset=0;
	ch3->SlaveGain=1;
	ch3->BacklashMode=BACKLASH_OFF;
	ch3->BacklashAmount=0;
	ch3->BacklashRate=0;
	ch3->invDistPerCycle=1;
	ch3->Lead=0;
	ch3->MaxFollowingError=10000;
	ch3->StepperAmplitude=20;

	ch3->iir[0].B0=1;
	ch3->iir[0].B1=0;
	ch3->iir[0].B2=0;
	ch3->iir[0].A1=0;
	ch3->iir[0].A2=0;

	ch3->iir[1].B0=1;
	ch3->iir[1].B1=0;
	ch3->iir[1].B2=0;
	ch3->iir[1].A1=0;
	ch3->iir[1].A2=0;

	ch3->iir[2].B0=0.000768809;
	ch3->iir[2].B1=0.00153762;
	ch3->iir[2].B2=0.000768809;
	ch3->iir[2].A1=1.92081;
	ch3->iir[2].A2=-0.923885;
	

	
	ch4->InputMode=NO_INPUT_MODE;
	ch4->OutputMode=STEP_DIR_MODE;
	ch4->Vel=40000;
	ch4->Accel=400000;
	ch4->Jerk=4e+06;
	ch4->P=0.2;
	ch4->I=0;
	ch4->D=0;
	ch4->FFAccel=0;
	ch4->FFVel=0;
	ch4->MaxI=200;
	ch4->MaxErr=200;
	ch4->MaxOutput=200;
	ch4->DeadBandGain=1;
	ch4->DeadBandRange=0;
	ch4->InputChan0=4;
	ch4->InputChan1=1;
	ch4->OutputChan0=63;
	ch4->OutputChan1=1;
	ch4->MasterAxis=-1;
	ch4->LimitSwitchOptions=0x120;
	ch4->LimitSwitchNegBit=0;
	ch4->LimitSwitchPosBit=0;
	ch4->SoftLimitPos=1e+09;
	ch4->SoftLimitNeg=-1e+09;
	ch4->InputGain0=0;
	ch4->InputGain1=1;
	ch4->InputOffset0=0;
	ch4->InputOffset1=0;
	ch4->OutputGain=1;
	ch4->OutputOffset=0;
	ch4->SlaveGain=1;
	ch4->BacklashMode=BACKLASH_OFF;
	ch4->BacklashAmount=0;
	ch4->BacklashRate=0;
	ch4->invDistPerCycle=1;
	ch4->Lead=0;
	ch4->MaxFollowingError=1000000000;
	ch4->StepperAmplitude=250;

	ch4->iir[0].B0=1;
	ch4->iir[0].B1=0;
	ch4->iir[0].B2=0;
	ch4->iir[0].A1=0;
	ch4->iir[0].A2=0;

	ch4->iir[1].B0=1;
	ch4->iir[1].B1=0;
	ch4->iir[1].B2=0;
	ch4->iir[1].A1=0;
	ch4->iir[1].A2=0;

	ch4->iir[2].B0=1;
	ch4->iir[2].B1=0;
	ch4->iir[2].B2=0;
	ch4->iir[2].A1=0;
	ch4->iir[2].A2=0;


	EnableAxisDest(X,0);
	EnableAxisDest(Y,0);
	EnableAxisDest(Z,0);
	EnableAxisDest(C,0);
	EnableAxisDest(A,0);
	
	DefineCoordSystem6(0,1,2,4,-1,3);
	
	
	for (;;)  					// loop forever
	{
		WaitNextTimeSlice();
		ServiceWatchdogStatus();
		DrivePower = ReadBit(142);
	
		if  ((Mach3Active &&	// Mach3 must be loaded for Mach3Active = 1
		      DrivePower  &&
		      !CycledOFF) &&
		    (ch0->Enable) &&
		    (ch1->Enable) &&
		    (ch2->Enable) &&		    		    
		    (ch3->Enable) &&
		    (ch4->Enable))		// Turns drives 'ON' IF ALL axes are enabled
		
			SetBit(155);		
		else
			ClearBit(155);		// Turns drives 'OFF' if ANY axis is disabled
			
		if (!DrivePower)
		{
			CycledOFF = 1;
		}
		
	}
		
	return 0;
}

void ServiceWatchdogStatus(void)
{
	static int Alive=FALSE;
	static int PrevStatusRequestCounter=-1;
	static double WatchdogTime=0;
	double T=Time_sec();
	DrivePower = ReadBit(142);
	
	// check if Host is requesting Status
	if (StatusRequestCounter != PrevStatusRequestCounter)
	{
		// yes, save time 
		WatchdogTime = T + WATCHDOG_DELAY;
		PrevStatusRequestCounter=StatusRequestCounter;
		if (!Alive) WatchdogOK();
		Alive=TRUE;
	}
	else
	{
		if (T > WatchdogTime)  // time to trigger?
		{
			if (Alive) WatchdogTripped();
			Alive=FALSE; 
		}
	}
}

void WatchdogOK(void)		// Trips when Mach3/KMotionCNC is open
{
	ClearBit(47);  			// KFLOP LED off
	Delay_sec(1);
	SetBit(47);
	Delay_sec(1);
}

void WatchdogTripped(void)	// Trips when Mach3/KMotionCNC is closed 
{	
	ClearBit(46);  			// KFLOP LED off
	Delay_sec(1);
	SetBit(46);
	Delay_sec(1);
	
	Mach3Active = 0;		// Keeps forever loop from re-enabling drives 
							// with 'SetBit(155)'
	ClearBit(155);			// Disables drives when Mach3 Program Closes
}

/code]

User avatar
TomKerekes
Posts: 2676
Joined: Mon Dec 04, 2017 1:49 am

Re: Limit switch setup options

Post by TomKerekes » Tue Nov 12, 2024 1:39 am

Hi Jim,

Normally KFLOP will allow you to Jog away from the limit but because the wiring/configuration sets both positive and negative limits Jogging in either direction is prevented.

The safest thing to do would be to wire to separate inputs.

Otherwise it might be possible to do a software trick and create separate virtual limit bits that are set based on monitoring the input and setting the appropriate virtual bit based on what direction the axis was moving when the input transitioned.
Regards,

Tom Kerekes
Dynomotion, Inc.

jpbarad7
Posts: 15
Joined: Wed Dec 07, 2022 6:25 pm

Re: Limit switch setup options

Post by jpbarad7 » Tue Nov 12, 2024 7:59 pm

Thanks Tom

The machine was equipped with an old Analam CNC system when I bought it 20 years ago. The X, Y, and Z axes each had a single limit switch that was triggered by adjustable bumpers at each end of travel. When I motorized and encoded the knee, I kept the concept similar.

Before reconfiguring the machine switches, I think I'll try fiddling with the software first.

Where is the code located that stops motion when a limit switches are triggered?

Jim

User avatar
TomKerekes
Posts: 2676
Joined: Mon Dec 04, 2017 1:49 am

Re: Limit switch setup options

Post by TomKerekes » Tue Nov 12, 2024 11:49 pm

Hi Jim,
Where is the code located that stops motion when a limit switches are triggered?
You just need to set the bit the axis is configured to watch as the limit.

Try this. Its a little tricky as we only want to set one limit then the limit is first entered. Otherwise trying to move out while still in the limit would still set both ends.

One issue I see is what to do if already in the limit on start up? This code will set the limit in whatever direction is first moved.

Code: Select all

#include "KMotionDef.h"

void SimulateLimits(CHAN *ch, int bit, int limit_polarity);

void main()
{
	for (;;)  // loop forever
	{
		SimulateLimits(ch0, 48, 1);  // Simulate virtual Pos/Neg limits for an axis
		
//		SetStateBit(48, ch0->Dest > 10000 || ch0->Dest < -1000);  // fake physical limit
	}
}

// Simulate pos and neg limits from a single input
// Axis must be configured to use Virtual bits for limits
// and to stop when high
void SimulateLimits(CHAN *ch, int bit, int limit_polarity)
{
	if (ReadBit(bit) == limit_polarity)  // limit switch hit?
	{
		if (!ReadBit(ch->LimitSwitchPosBit) && // only set one until back out of limit
			!ReadBit(ch->LimitSwitchNegBit))
		{
			if (ch->DirectionOfMotion == 1)  // going positive?
				SetBit(ch->LimitSwitchPosBit);

			if (ch->DirectionOfMotion == -1)  // going negative?
				SetBit(ch->LimitSwitchNegBit);
		}
	}
	else // out of limits?
	{
		ClearBit(ch->LimitSwitchPosBit);
		ClearBit(ch->LimitSwitchNegBit);
	}
}

I used Virtual Bits 49 and 50 for testing

VirtualLimits.png
VirtualLimits.png (3.72 KiB) Viewed 138 times
Attachments
SimulatePosNegLimits.c
(1006 Bytes) Downloaded 10 times
Regards,

Tom Kerekes
Dynomotion, Inc.

Post Reply