Code:
//V3- Added LowPass Filter to output.
//V3.1- Removed waylube bit timer
//V3.2-Added Auto Tool Changer Code
#include "KMotionDef.h"
#define TMP 10 // which spare persist to use to transfer data
#include "KflopToKMotionCNCFunctions.c"
#include "AdjustSoftLimits.c"
//#define CYCLESTARTBIT 16
//#define FEEDHOLDBIT 17
#define ESTOP 137
#define MISTCOOLANTBIT 158
#define FLOODCOOLANTBIT 157
//#define SPINDLESTOPBIT 21
//#define SPINDLECWBIT 19
//#define SPINDLECCWBIT 20
#define VFDERRORLINEBIT 138
#define XSDERRORLINEBIT 139
#define YSDERRORLINEBIT 140
#define ZSDERRORLINEBIT 141
#define XMINUSLIMITBIT 1025
#define XPLUSLIMITBIT 1024
#define YMINUSLIMITBIT 1027
#define YPLUSLIMITBIT 1026
#define ZMINUSLIMITBIT 1029
#define ZPLUSLIMITBIT 1028
#define X 0
#define Y 1
#define Z 2
#define QA 1042 // define to which IO bits the AB signals are connected
#define QB 1041
#define TAU 0.08 // smoothness factor (Low Pass Time constant seconds)
#define FINAL_TIME 1.0 // Set final dest after this amount of time with no change
#define MAX_JOG_SPEED_X 510.0 // ipm
#define MAX_JOG_SPEED_Y 510.0 // ipm
#define MAX_JOG_SPEED_Z 510.0 // ipm
void UpdateJogSpeeds(void);
void DoLimitJog(double rate, int cmd);
void ServiceTimerSequence(void);
void ServiceEStop(void);
// function prototypes for compiler
int DoPC(int cmd);
int DoPCFloat(int cmd, float f);
int Debounce(int n, int *cnt, int *last, int *lastsolid);
// state variables for switch debouncing
int flast = 0, flastsolid = -1, fcount = 0;
int ccwlast = 0, ccwlastsolid = -1, ccwcount = 0;
int cwlast = 0, cwlastsolid = -1, cwcount = 0;
int colast = 0, colastsolid = -1, cocount = 0;
int solast = 0, solastsolid = -1, socount = 0;
int xsdelast = 0, xsdelastsolid = -1, xsdecount = 0;
int ysdelast = 0, ysdelastsolid = -1, ysdecount = 0;
int zsdelast = 0, zsdelastsolid = -1, zsdecount = 0;
int vfdelast = 0, vfdelastsolid = -1, vfdecount = 0;
int zmlast = 0, zmlastsolid = -1, zmcount = 0;
int zplast = 0, zplastsolid = -1, zpcount = 0;
int ymlast = 0, ymlastsolid = -1, ymcount = 0;
int yplast = 0, yplastsolid = -1, ypcount = 0;
int xmlast = 0, xmlastsolid = -1, xmcount = 0;
int xplast = 0, xplastsolid = -1, xpcount = 0;
int clast = 0, clastsolid = -1, ccount = 0;
int elast = 0, elastsolid = -1, ecount = 0;
int hlast = 0, hlastsolid = -1, hcount = 0;
int rlast = 0, rlastsolid = -1, rcount = 0;
int zlast = 0, zlastsolid = -1, zcount = 0;
int xpjblast = 0, xpjblastsolid = -1, xpjbcount = 0;
//int Debounce(int n, int *cnt, int *last, int *lastsolid);
main()
{
InitAux();
AddKonnect(0, &VirtualBits, VirtualBitsEx);
int BitA, Change1 = 0, Change2 = 0, DiffX2;
int PosNoWrap, NewPos, Pos = 0, wraps;
int InMotion = FALSE, Axis, LastAxis = -1;
double LastChangeTime = 0, Target, Factor = 0;
#define DROIN 40
double *pin = (double *)&persist.UserData[(DROIN - 1) * 2];
int result;
int Answer;
ch0->InputMode = ENCODER_MODE;
ch0->OutputMode = CL_STEP_DIR_MODE;
ch0->Vel = 270700;
ch0->Accel = 9.7e+05;
ch0->Jerk = 10e+06;
ch0->P = 0;
ch0->I = 0.003;
ch0->D = 0;
ch0->FFAccel = 0;
ch0->FFVel = 0;
ch0->MaxI = 400;
ch0->MaxErr = 200;
ch0->MaxOutput = 200;
ch0->DeadBandGain = 0;
ch0->DeadBandRange = 1;
ch0->InputChan0 = 0;
ch0->InputChan1 = 0;
ch0->OutputChan0 = 12;
ch0->OutputChan1 = 0;
ch0->MasterAxis = -1;
ch0->LimitSwitchOptions = 0x113;
ch0->LimitSwitchNegBit = 1025;
ch0->LimitSwitchPosBit = 1024;
ch0->SoftLimitPos = 1e+09;
ch0->SoftLimitNeg = -1e+09;
ch0->InputGain0 = -1;
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 = 100;
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.000768809;
ch0->iir[2].B1 = 0.00153762;
ch0->iir[2].B2 = 0.000768809;
ch0->iir[2].A1 = 1.92081;
ch0->iir[2].A2 = -0.923885;
ch1->InputMode = ENCODER_MODE;
ch1->OutputMode = CL_STEP_DIR_MODE;
ch1->Vel = 270700;
ch1->Accel = 9.7e+05;
ch1->Jerk = 10e+06;
ch1->P = 0;
ch1->I = 0.003;
ch1->D = 0;
ch1->FFAccel = 0;
ch1->FFVel = 0;
ch1->MaxI = 200;
ch1->MaxErr = 200;
ch1->MaxOutput = 200;
ch1->DeadBandGain = 0;
ch1->DeadBandRange = 0;
ch1->InputChan0 = 1;
ch1->InputChan1 = 0;
ch1->OutputChan0 = 13;
ch1->OutputChan1 = 0;
ch1->MasterAxis = -1;
ch1->LimitSwitchOptions = 0x112;
ch1->LimitSwitchNegBit = 1026;
ch1->LimitSwitchPosBit = 1026;
ch1->SoftLimitPos = 1e+09;
ch1->SoftLimitNeg = -286500;
ch1->InputGain0 = 1;
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 = 500;
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.000768809;
ch1->iir[2].B1 = 0.00153762;
ch1->iir[2].B2 = 0.000768809;
ch1->iir[2].A1 = 1.92081;
ch1->iir[2].A2 = -0.923885;
ch2->InputMode = ENCODER_MODE;
ch2->OutputMode = CL_STEP_DIR_MODE;
ch2->Vel = 270700;
ch2->Accel = 7.7e+05;
ch2->Jerk = 3.9e+06;
ch2->P = 0;
ch2->I = 0.003;
ch2->D = 0;
ch2->FFAccel = 0;
ch2->FFVel = 0;
ch2->MaxI = 400;
ch2->MaxErr = 155;
ch2->MaxOutput = 200;
ch2->DeadBandGain = 0;
ch2->DeadBandRange = 0;
ch2->InputChan0 = 2;
ch2->InputChan1 = 0;
ch2->OutputChan0 = 14;
ch2->OutputChan1 = 0;
ch2->MasterAxis = -1;
ch2->LimitSwitchOptions = 0x113;
ch2->LimitSwitchNegBit = 1029;
ch2->LimitSwitchPosBit = 1028;
ch2->SoftLimitPos = 1e+09;
ch2->SoftLimitNeg = -1e+09;
ch2->InputGain0 = 1;
ch2->InputGain1 = 1;
ch2->InputOffset0 = 0;
ch2->InputOffset1 = 0;
ch2->OutputGain = 1;
ch2->OutputOffset = 0;
ch2->SlaveGain = 1;
ch2->BacklashMode = BACKLASH_LINEAR;
ch2->BacklashAmount = 5;
ch2->BacklashRate = 90;
ch2->invDistPerCycle = 1;
ch2->Lead = 0;
ch2->MaxFollowingError = 500;
ch2->StepperAmplitude = 20;
ch2->iir[0].B0 = 1.000000;
ch2->iir[0].B1 = 0.000000;
ch2->iir[0].B2 = 0.000000;
ch2->iir[0].A1 = 0.000000;
ch2->iir[0].A2 = 0.000000;
ch2->iir[1].B0 = 1.000000;
ch2->iir[1].B1 = 0.000000;
ch2->iir[1].B2 = 0.000000;
ch2->iir[1].A1 = 0.000000;
ch2->iir[1].A2 = 0.000000;
ch2->iir[2].B0 = 0.000769;
ch2->iir[2].B1 = 0.001538;
ch2->iir[2].B2 = 0.000769;
ch2->iir[2].A1 = 1.920810;
ch2->iir[2].A2 = -0.923885;
EnableAxisDest(0, ch0->Position);
//EnableAxisDest(0,ch0->Dest);
EnableAxisDest(1, ch1->Position);
//EnableAxisDest(1,ch1->Dest);
EnableAxisDest(2, ch2->Position);
//EnableAxisDest(2,ch2->Dest);
FILE *f;
double CTool = 0;
f = fopen("c:\\KMotionFiles\\KFlopData.txt", "rt");
if (!f)
{
printf("Unable to open file\n");
return;
}
fscanf(f, "%lf", &CTool);
fclose(f);
persist.UserData[157] = CTool;
printf("Current tool=%f\n", CTool);
// Send MDI code to kmotion to display proper tool
char s[80];
sprintf(s, "T%d", persist.UserData[157]);
MDI(s); //send the T word
SetBit(144); //Enables Main Contactor Relay1 for servo drives with SWE kanalog output.
// once the RESET button is clicked.
SetBit(145); //Enables Relay2 for enable/disable of servo drives and VFDwith SWE
// kanalog output. And controled by Estop switch.
SetBit(159); // WayLubePump
DefineCoordSystem(0, 1, 2, -1); //DefineXYZ(A not used)
{
double Tau = 0.002; //seconds for low pass filtertime constant
KLP = exp(-TIMEBASE / Tau);
printf("Tau=%f KLP=%f\n", Tau, KLP);
}
for (;;) // loop forever
{
// convert quadrature to 2 bit binary
BitA = ReadBit(QA);
PosNoWrap = (ReadBit(QB) ^ BitA) | (BitA << 1);
// Diff between expected position based on average of two prev deltas
// and position with no wraps. (Keep as X2 to avoid division by 2)
DiffX2 = 2 * (Pos - PosNoWrap); // + (Change2+Change1);
// Calc quadrature wraparounds to bring Diff nearest zero
// offset by 128 wraps to avoid requiring floor()
wraps = ((DiffX2 + 1028) >> 3) - 128;
// factor in the quadrature wraparounds
NewPos = PosNoWrap + (wraps << 2);
Change2 = Change1;
Change1 = NewPos - Pos;
Pos = NewPos;
if (ReadBit(1048)) // is X1 selected?
Factor = .5;
else if (ReadBit(1046)) // is X10 selected?
Factor = 5;
else if (ReadBit(1045)) // is X100 selected?
Factor = 50.;
if (ReadBit(1052)) // is x selected?
Axis = 0;
else if (ReadBit(1055)) // is y selected?
Axis = 1;
else if (ReadBit(1053)) // is z selected?
Axis = 2;
else // ignore encoder
Change1 = 0;
// check if the Axis just changed or we have been
// converging to the target for a long time
if (Axis != LastAxis || (InMotion && Time_sec() > LastChangeTime + FINAL_TIME))
{
if (InMotion)
Move(LastAxis, Target); //finalize any motion
LastAxis = Axis;
InMotion = FALSE;
}
if (Change1) // did we move?
{
if (!InMotion)
Target = chan[Axis].Dest;
Target += Change1 * Factor;
MoveExp(Axis, Target, TAU); // note: contains a WaitNextTimeSlice
LastChangeTime = Time_sec();
InMotion = TRUE;
}
else
{
WaitNextTimeSlice(); // execute loop once every time slice
// check if all axes are enabled
if (ch0->Enable && ch1->Enable && ch2->Enable)
{
SetBit(145); // yes they are, enable the amplifier
}
else
{
ClearBit(145); // no at least one is disabled, disable the amplifier
}
ServiceTimerSequence(); // service the timer sequencing
UpdateJogSpeeds();
} // end of forever loop
}
}
//set Rate within Limits
void DoLimitJog(double rate, int cmd)
{
if (rate < 0.0)
rate = 0.0;
if (rate > 1.0)
rate = 1.0;
DoPCFloat(cmd, rate);
}
void UpdateJogSpeeds(void)
{
static LastChangeCount = -1;
int Units, TWORD, HWORD, DWORD;
double rate;
// don't bother if nothing changed
if (EditChangeCount == LastChangeCount)
return;
// remember count before updating
LastChangeCount = EditChangeCount;
// Read double from a KMotionCNC Edit Control
// Persist Var identifies the Control and contents specifies
// where the string data should be placed in the
// Gather Buffer as an offset in words
if (GetEditControlDouble(&rate, 170, 1000))
return; // exit if value is invalid
GetMiscSettings(&Units, &TWORD, &HWORD, &DWORD); // check Units
if (Units == CANON_UNITS_MM)
rate = rate / 25.4; // convert screen value to ipm
DoLimitJog(rate / MAX_JOG_SPEED_X, PC_COMM_SET_JOG_OVERRIDE_X);
DoLimitJog(rate / MAX_JOG_SPEED_Y, PC_COMM_SET_JOG_OVERRIDE_Y);
DoLimitJog(rate / MAX_JOG_SPEED_Z, PC_COMM_SET_JOG_OVERRIDE_Z);
}
// sequence an IO Bit Based on Time in a non-blocking manner WAY LUBE PUMP
//#define TIME_ON 1800.0//seconds
//#define CYCLE_TIME 700.0 //seconds
//#define OUTPUT_BIT 159 // which IO bit to drive
void ServiceTimerSequence(void)
{
// static double T0=0.0; // remember the last time we turned on
//double T=Time_sec(); // get current Time_sec
//if (T0==0.0 || T > T0 + CYCLE_TIME) T0=T; // set start time of cycle
//if (T < T0 + TIME_ON) // are we within the TIME_ON section of the cycle?
// SetBit(OUTPUT_BIT); //yes
//else
// ClearBit(OUTPUT_BIT); //no
int result;
/// Handle FeedHold/Resume
// result = Debounce(ReadBit(FEEDHOLDBIT),&fcount,&flast,&flastsolid);
// if (result == 1)
//{
//if (CS0_StoppingState == 0)
//StopCoordinatedMotion();
//else
//ResumeCoordinatedMotion();
//}
/// Handle Cycle Start
//result = Debounce(ReadBit(CYCLESTARTBIT),&ccount,&clast,&clastsolid);
//if (result == 1)
//{
//DoPC(PC_COMM_EXECUTE);
//}
/// Handle Coolant Pump
//result = Debounce(ReadBit(COOLANTBIT),&cocount,&colast,&colastsolid);
//if (result == 1)
//{
//if (ReadBit(154) == 1)
//ClearBit(154);
//else
//SetBit(154);
//}
//}
/// Handle ESTOP
result = Debounce(ReadBit(ESTOP), &ecount, &elast, &elastsolid);
if (result == 0)
{
DoPC(PC_COMM_ESTOP);
ClearBit(145); //Disable Servo Drives & vfd
ClearBit(157); //Turn off FloodCoolant
ClearBit(158); //Turn off Mist Coolant
ClearBit(159); //WayLubePump
ClearBit(63); //TC_OUT Solenoid
ClearBit(62); //TC_IN Solenoid
ClearBit(61); //TC_DOWN Solenoid
ClearBit(60); //TC_UP Solenoid
ClearBit(59); //Tool Release Solenoid
ClearBit(48); //ZeroServoCommand to VFD
ClearBit(49); //TC_CW
ClearBit(50); //TC_CCW
ClearBit(156); //SpindleOrientateProx minusVolt
SetBit(147); // turn off spindle clockwise
SetBit(148); // turn off spindle counterclockwise
MsgBox("E-STOP Condition", MB_OK | MB_ICONEXCLAMATION);
}
// Handle X Axis Servo Drives Error Line
result = Debounce(ReadBit(XSDERRORLINEBIT), &xsdecount, &xsdelast, &xsdelastsolid);
if (result == 1)
{
DoPC(PC_COMM_ESTOP);
ClearBit(145); //Disable Servo Drives & vfd
SetBit(147); // turn off spindle clockwise
SetBit(148); // turn off spindle counterclockwise
ClearBit(157); //Turn off FloodCoolant
ClearBit(158); //Turn off Mist Coolant
MsgBox("X ServoDrive Fault", MB_OK | MB_ICONEXCLAMATION);
ClearBit(144); //Enable/Disable MainContactorRelay for servo drive main & logic power.
}
// Handle Y Axis Servo Drives Error Line
result = Debounce(ReadBit(YSDERRORLINEBIT), &ysdecount, &ysdelast, &ysdelastsolid);
if (result == 1)
{
DoPC(PC_COMM_ESTOP);
ClearBit(145); //Disable Servo Drives & vfd
SetBit(147); // turn off spindle clockwise
SetBit(148); // turn off spindle counterclockwise
ClearBit(157); //Turn off FloodCoolant
ClearBit(158); //Turn off Mist Coolant
MsgBox("Y ServoDrive Fault", MB_OK | MB_ICONEXCLAMATION);
ClearBit(144); //Enable/Disable MainContactorRelay for servo drive main & logic power.
}
// Handle Z Axis Servo Drives Error Line
result = Debounce(ReadBit(ZSDERRORLINEBIT), &zsdecount, &zsdelast, &zsdelastsolid);
if (result == 1)
{
DoPC(PC_COMM_ESTOP);
ClearBit(145); //Disable Servo Drives & vfd
SetBit(147); // turn off spindle clockwise
SetBit(148); // turn off spindle counterclockwise
ClearBit(157); //Turn off FloodCoolant
ClearBit(158); //Turn off Mist Coolant
MsgBox("Z ServoDrive Fault", MB_OK | MB_ICONEXCLAMATION);
ClearBit(144); //Enable/Disable MainContactorRelay for servo drive main & logic power.
}
// Handle VFD Error Output
result = Debounce(ReadBit(VFDERRORLINEBIT), &vfdecount, &vfdelast, &vfdelastsolid);
if (result == 1)
{
DoPC(PC_COMM_ESTOP);
ClearBit(145); //Disable Servo Drives & vfd
SetBit(147); // turn off spindle clockwise
SetBit(148); // turn off spindle counterclockwise
ClearBit(157); //Turn off FloodCoolant
ClearBit(158); //Turn off Mist Coolant
MsgBox("VFD Fault", MB_OK | MB_ICONEXCLAMATION);
}
// Handle axis X minus limit switch
result = Debounce(ReadBit(XMINUSLIMITBIT), &xmcount, &xmlast, &xmlastsolid);
if (result == 1)
{
ClearBit(152);
MsgBox("X - Limit. Jog opposite direction.", MB_OK | MB_ICONEXCLAMATION);
}
// Handle axis X plus limit switch
result = Debounce(ReadBit(XPLUSLIMITBIT), &xpcount, &xplast, &xplastsolid);
if (result == 1)
{
ClearBit(152);
MsgBox("X + Limit. Jog opposite direction.", MB_OK | MB_ICONEXCLAMATION);
}
// Handle axis Y minus limit switch
result = Debounce(ReadBit(YMINUSLIMITBIT), &ymcount, &ymlast, &ymlastsolid);
if (result == 1)
{
ClearBit(152);
MsgBox("Y - Limit. Jog opposite direction.", MB_OK | MB_ICONEXCLAMATION);
}
// Handle axis Y plus limit switch
result = Debounce(ReadBit(YPLUSLIMITBIT), &ypcount, &yplast, &yplastsolid);
if (result == 1)
{
ClearBit(152);
MsgBox("Y + Limit. Jog opposite direction.", MB_OK | MB_ICONEXCLAMATION);
}
// Handle axis Z minus limit switch
result = Debounce(ReadBit(ZMINUSLIMITBIT), &zmcount, &zmlast, &zmlastsolid);
if (result == 1)
{
ClearBit(152);
MsgBox("Z - Limit. Jog opposite direction.", MB_OK | MB_ICONEXCLAMATION);
}
// Handle axis Z plus limit switch
result = Debounce(ReadBit(ZPLUSLIMITBIT), &zpcount, &zplast, &zplastsolid);
if (result == 1)
{
ClearBit(152);
MsgBox("Z + Limit. Jog opposite direction.", MB_OK | MB_ICONEXCLAMATION);
}
// Handle HALT
//result = Debounce(ReadBit(HALTBIT),&hcount,&hlast,&hlastsolid);
//if (result == 1)
//{
}
// Put a Float as a parameter and pass the command to the App
int DoPCFloat(int cmd, float f)
{
int result;
persist.UserData[PC_COMM_PERSIST + 1] = *(int *)&f;
return DoPC(cmd);
}
// Pass a command to the PC and wait for it to handshake
// that it was received by either clearing the command
// or changing it to a negative error code
int DoPC(int cmd)
{
int result;
persist.UserData[PC_COMM_PERSIST] = cmd;
do
{
WaitNextTimeSlice();
}
while (result = persist.UserData[PC_COMM_PERSIST] > 0);
printf("Result = %d\n", result);
return result;
}
// Debounce a bit
//
// return 1 one time when first debounced high
// return 0 one time when first debounced low
// return -1 otherwise
#define DBTIME 300
int Debounce(int n, int *cnt, int *last, int *lastsolid)
{
int v = -1;
if (n == *last) // same as last time?
{
if (*cnt == DBTIME - 1)
{
if (n != *lastsolid)
{
v = *lastsolid = n; // return debounced value
}
}
if (*cnt < DBTIME)
(*cnt)++;
}
else
{
*cnt = 0; // reset count
}
*last = n;
return v;
}