/****************************************************************************** * * Copyright (C) 2007 Peter G. Vavaroutsos * * * This module is a hard coded PLC for use on a Bridgeport Boss. * * Installation of the component (realtime only): * * insmod boss_plc count=<1> * * * The following items are exported to the HAL. is * the component id number and is formated as "%d". * * Pins (former parameters): * u32 boss_plc..amp-ready-delay * u32 boss_plc..brake-on-delay * u32 boss_plc..brake-off-delay * u32 boss_plc..spindle-lo-to-hi * float boss_plc..jog-scale-0 * float boss_plc..jog-scale-1 * float boss_plc..jog-scale-2 * * Pins: * bit boss_plc..cycle-start-in * bit boss_plc..cycle-hold-in * bit boss_plc..feed-hold-out * float boss_plc..adaptive-feed-in * float boss_plc..adaptive-feed-out * bit boss_plc..tool-change-in * bit boss_plc..tool-changed-out * bit boss_plc..wait-user-out * bit boss_plc..mist-on-in * bit boss_plc..mist-on-out * bit boss_plc..flood-on-in * bit boss_plc..flood-on-out * * bit boss_plc..limit-override-in * bit boss_plc..limit-active-out * float boss_plc..x-position-in * bit boss_plc..x-jog-en-in * bit boss_plc..x-limit-in * bit boss_plc..x-limit-pos-out * bit boss_plc..x-limit-neg-out * float boss_plc..y-position-in * bit boss_plc..y-jog-en-in * bit boss_plc..y-limit-in * bit boss_plc..y-limit-pos-out * bit boss_plc..y-limit-neg-out * bit boss_plc..z-jog-en-in * bit boss_plc..z-limit-pos-in * bit boss_plc..z-limit-neg-in * bit boss_plc..z-limit-pos-out * bit boss_plc..z-limit-neg-out * * bit boss_plc..x-amp-enable-in * bit boss_plc..x-amp-ready-in * bit boss_plc..x-amp-fault-out * bit boss_plc..y-amp-enable-in * bit boss_plc..y-amp-ready-in * bit boss_plc..y-amp-fault-out * bit boss_plc..z-amp-enable-in * bit boss_plc..z-amp-ready-in * bit boss_plc..z-amp-fault-out * bit boss_plc..a-amp-enable-in * bit boss_plc..a-amp-ready-in * bit boss_plc..a-amp-fault-out * * float boss_plc..spindle-speed-in * bit boss_plc..spindle-is-on-in * bit boss_plc..spindle-fwd-out * bit boss_plc..spindle-rev-out * bit boss_plc..spindle-inc-in * bit boss_plc..spindle-dec-in * bit boss_plc..spindle-inc-out * bit boss_plc..spindle-dec-out * bit boss_plc..brake-en-in * bit boss_plc..brake-en-out * * bit boss_plc..jog-sel-in-0 * bit boss_plc..jog-sel-in-1 * bit boss_plc..jog-sel-in-2 * bit boss_plc..jog-scale-out * * Functions: * void boss_plc..refresh * ****************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General * Public License as published by the Free Software Foundation. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA * * THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR * ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE * TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of * harming persons must have provisions for completely removing power * from all motors, etc, before persons enter any danger area. All * machinery must be designed to comply with local and national safety * codes, and the authors of this software can not, and do not, take * any responsibility for such compliance. * * This code was written as part of the EMC HAL project. For more * information, go to www.linuxcnc.org. * ******************************************************************************/ #include "rtapi.h" // RTAPI realtime OS API. #include "rtapi_app.h" // RTAPI realtime module decls. #include "hal.h" // HAL public API decls. #define TRUE 1 #define FALSE 0 typedef int BOOL; // Module information. MODULE_AUTHOR("Pete Vavaroutsos"); MODULE_DESCRIPTION("Bridgeport BOSS PLC for EMC HAL"); MODULE_LICENSE("GPL"); static unsigned long count = 1; RTAPI_MP_LONG(count, "Number of BOSS PLCs to instance"); static int debug = 0; RTAPI_MP_INT(debug, "Enables optional params"); /****************************************************************************** * TIMER OBJECT * * This object implements a timer with mSec resolution. * ******************************************************************************/ typedef enum { TM_ONE_SHOT, TM_CONTINUOUS, } TimerMode; typedef void (*TIMER_ROUTINE)(void *pArgs); typedef struct { // Private data. BOOL enabled; hal_u32_t nSec; hal_u32_t count; hal_u32_t timeout; TIMER_ROUTINE pTimeout; void *pArgs; TimerMode mode; } Timer; static void Timer_Init(Timer *this); static void Timer_Enable(Timer *this, TimerMode mode); static void Timer_Disable(Timer *this); static BOOL Timer_IsEnabled(Timer *this); static void Timer_Update(Timer *this, long period); static void Timer_SetTimeout(Timer *this, hal_u32_t timeout); #if 0 static void Timer_SetCallback(Timer *this, TIMER_ROUTINE pCallback, void *pArgs); #endif /****************************************************************************** * LIMIT OBJECT * * This object converts a bi-directional limit switch into to limit signals. * ******************************************************************************/ typedef enum { LS_INIT, LS_ON_LIMIT, LS_NO_MOTION, LS_POS_MOTION, LS_NEG_MOTION, } LimitState; typedef struct { // Exported pins. hal_float_t *pPositionIn; hal_bit_t *pJogEnIn; hal_bit_t *pIn; hal_bit_t *pPosOut; hal_bit_t *pNegOut; // Internal data. LimitState state; hal_float_t position; hal_bit_t limitPos; hal_bit_t limitNeg; } Limit; static int Limit_Export(Limit *this, int compId, int id, char *name, char axis); static void Limit_Init(Limit *this); static BOOL Limit_IsActive(Limit *this); static void Limit_Refresh(Limit *this, hal_bit_t override); /****************************************************************************** * AMP OBJECT * * This object creates the amp fault signal from the enable and ready signal. * ******************************************************************************/ typedef struct { // Exported pins. hal_bit_t *pEnableIn; hal_bit_t *pReadyIn; hal_bit_t *pFaultOut; // Internal data. Timer timer; hal_bit_t lastEnable; } Amp; static int Amp_Export(Amp *this, int compId, int id, char *name, char axis); static void Amp_Init(Amp *this); static void Amp_Refresh(Amp *this, long period, hal_u32_t readyDelay); /****************************************************************************** * PLC OBJECT * * This object contains all the data for one PLC. A component object is * dynamically allocated in shmem for each PLC during initialization. * ******************************************************************************/ #define NUM_JOG_SEL 3 #define NUM_AXIS 4 static char axisNames[NUM_AXIS] = { 'x', 'y', 'z', 'a' }; typedef enum { SS_OFF, SS_WAIT_BRAKE_OFF, SS_WAIT_ON, SS_ON, SS_WAIT_OFF, SS_WAIT_BRAKE_ON, } SpindleState; typedef struct { // Pins. (former parameters) hal_u32_t *ampReadyDelay; hal_u32_t *brakeOnDelay; hal_u32_t *brakeOffDelay; hal_float_t *spindleLoToHi; hal_float_t *jogScale[NUM_JOG_SEL]; // Pins. hal_bit_t *pCycleStartIn; hal_bit_t *pCycleHoldIn; hal_bit_t *pFeedHoldOut; hal_float_t *pAdaptiveFeedIn; hal_float_t *pAdaptiveFeedOut; hal_bit_t *pToolChangeIn; hal_bit_t *pToolChangedOut; hal_bit_t *pWaitUserOut; hal_bit_t *pMistOnIn; hal_bit_t *pMistOnOut; hal_bit_t *pFloodOnIn; hal_bit_t *pFloodOnOut; hal_bit_t *pLimitOverrideIn; hal_bit_t *pLimitActiveOut; Limit xLimit; Limit yLimit; hal_bit_t *pZJogEnIn; hal_bit_t *pZLimitPosIn; hal_bit_t *pZLimitNegIn; hal_bit_t *pZLimitPosOut; hal_bit_t *pZLimitNegOut; Amp amps[NUM_AXIS]; hal_float_t *pSpindleSpeedIn; hal_bit_t *pSpindleIsOnIn; hal_bit_t *pSpindleFwdOut; hal_bit_t *pSpindleRevOut; hal_bit_t *pSpindleIncIn; hal_bit_t *pSpindleDecIn; hal_bit_t *pSpindleIncOut; hal_bit_t *pSpindleDecOut; hal_bit_t *pBrakeEnIn; hal_bit_t *pBrakeEnOut; hal_bit_t *pJogSelIn[NUM_JOG_SEL]; hal_float_t *pJogScaleOut; // Private data. SpindleState spindleState; Timer spindleTimer; hal_float_t lastSpindleSpeed; hal_bit_t lastCycleStart; } Plc; // These methods are used for initialization. static int Plc_Init(Plc *this); static int Plc_Export(Plc *this, int compId, int id); static int Plc_ExportFeed(Plc *this, int compId, int id, char *name); static int Plc_ExportLimits(Plc *this, int compId, int id, char *name); static int Plc_ExportAmps(Plc *this, int compId, int id, char *name); static int Plc_ExportSpindle(Plc *this, int compId, int id, char *name); static int Plc_ExportJog(Plc *this, int compId, int id, char *name); // These methods are exported to the HAL. static void Plc_Refresh(void *this, long period); // Private helper methods. static void Plc_RefreshFeed(Plc *this, long period); static void Plc_RefreshLimits(Plc *this, long period); static void Plc_RefreshAmps(Plc *this, long period); static void Plc_RefreshSpindle(Plc *this, long period); static void Plc_RefreshJog(Plc *this, long period); /****************************************************************************** * COMPONENT OBJECT * * This object contains all the data for this HAL component. * ******************************************************************************/ #define MAX_DEVICES 4 typedef struct { int id; // HAL component ID. Plc *plcTable[MAX_DEVICES]; } Component; static Component component; /****************************************************************************** * INIT AND EXIT CODE ******************************************************************************/ int rtapi_app_main(void) { int i; Plc *pComp; // Connect to the HAL. component.id = hal_init("boss_plc"); if (component.id < 0) { rtapi_print_msg(RTAPI_MSG_ERR, "BOSS_PLC: ERROR: hal_init() failed\n"); return(-1); } for(i = 0; i < MAX_DEVICES; i++){ component.plcTable[i] = NULL; } if(count > MAX_DEVICES) count = MAX_DEVICES; for(i = 0; i < count; i++){ // Allocate memory for device object. pComp = hal_malloc(sizeof(Plc)); if (pComp == NULL) { rtapi_print_msg(RTAPI_MSG_ERR, "BOSS_PLC: ERROR: hal_malloc() failed\n"); hal_exit(component.id); return(-1); } // Save pointer to device object. component.plcTable[i] = pComp; // Initialize device. if(Plc_Init(pComp)){ hal_exit(component.id); return(-1); } // Export pins, parameters, and functions. if(Plc_Export(pComp, component.id, i)){ hal_exit(component.id); return(-1); } } hal_ready(component.id); return(0); } void rtapi_app_exit(void) { int i; Plc *pComp; hal_exit(component.id); for(i = 0; i < MAX_DEVICES; i++){ if((pComp = component.plcTable[i]) != NULL){ // TODO: Free device object when HAL supports free. // hal_free(pComp); } } } /****************************************************************************** * PLC OBJECT FUNCTION DEFINITIONS ******************************************************************************/ /* * LOCAL FUNCTIONS */ static int Plc_Init(Plc *this) { int i; // Initialize variables. this->spindleState = SS_OFF; this->lastSpindleSpeed = 0.0; this->lastCycleStart = 1; // Initialize parameters. *(this->brakeOffDelay) = 500; *(this->brakeOnDelay) = 300; *(this->ampReadyDelay) = 50; *(this->spindleLoToHi) = 500; *(this->jogScale[0]) = 0.0001; for(i = 1; i < NUM_JOG_SEL; i++){ *(this->jogScale[i]) = *(this->jogScale[i-1]) * 10; } // Initialize timer. Timer_Init(&this->spindleTimer); // Initialize limits. Limit_Init(&this->xLimit); Limit_Init(&this->yLimit); // Initialize amps. for(i = 0; i < NUM_AXIS; i++){ Amp_Init(&this->amps[i]); } return(0); } static int Plc_Export(Plc *this, int compId, int id) { int msgLevel, error; char name[HAL_NAME_LEN + 1]; // This function exports a lot of stuff, which results in a lot of // logging if msg_level is at INFO or ALL. So we save the current value // of msg_level and restore it later. If you actually need to log this // function's actions, change the second line below. msgLevel = rtapi_get_msg_level(); rtapi_set_msg_level(RTAPI_MSG_WARN); // Export pins and parameters. error = Plc_ExportFeed(this, compId, id, name); if(!error){ error = Plc_ExportLimits(this, compId, id, name); } if(!error){ error = Plc_ExportAmps(this, compId, id, name); } if(!error){ error = Plc_ExportSpindle(this, compId, id, name); } if(!error){ error = Plc_ExportJog(this, compId, id, name); } // Export functions. if(!error){ rtapi_snprintf(name, sizeof(name), "boss_plc.%d.refresh", id); error = hal_export_funct(name, Plc_Refresh, this, 1, 0, compId); } // Restore saved message level. rtapi_set_msg_level(msgLevel); return(error); } static int Plc_ExportFeed(Plc *this, int compId, int id, char *name) { int error; // Export pins. error = hal_pin_bit_newf(HAL_IN, &this->pCycleStartIn, compId, "boss_plc.%d.cycle-start-in", id); if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pCycleHoldIn, compId, "boss_plc.%d.cycle-hold-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pFeedHoldOut, compId, "boss_plc.%d.feed-hold-out", id); } if(!error){ error = hal_pin_float_newf(HAL_IN, &this->pAdaptiveFeedIn, compId, "boss_plc.%d.adaptive-feed-in", id); } if(!error){ *this->pAdaptiveFeedIn = 1.0; error = hal_pin_float_newf(HAL_OUT, &this->pAdaptiveFeedOut, compId, "boss_plc.%d.adaptive-feed-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pToolChangeIn, compId, "boss_plc.%d.tool-change-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pToolChangedOut, compId, "boss_plc.%d.tool-changed-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pWaitUserOut, compId, "boss_plc.%d.wait-user-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pMistOnIn, compId, "boss_plc.%d.mist-on-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pMistOnOut, compId, "boss_plc.%d.mist-on-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pFloodOnIn, compId, "boss_plc.%d.flood-on-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pFloodOnOut, compId, "boss_plc.%d.flood-on-out", id); } return(error); } static int Plc_ExportLimits(Plc *this, int compId, int id, char *name) { int error; // Export pins. error = hal_pin_bit_newf(HAL_IN, &this->pLimitOverrideIn, compId, "boss_plc.%d.limit-override-in", id); if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pLimitActiveOut, compId, "boss_plc.%d.limit-active-out", id); } if(!error){ error = Limit_Export(&this->xLimit, compId, id, name, axisNames[0]); } if(!error){ error = Limit_Export(&this->yLimit, compId, id, name, axisNames[1]); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pZLimitPosIn, compId, "boss_plc.%d.%c-limit-pos-in", id, axisNames[2]); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pZJogEnIn, compId, "boss_plc.%d.%c-jog-en-in", id, axisNames[2]); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pZLimitNegIn, compId, "boss_plc.%d.%c-limit-neg-in", id, axisNames[2]); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pZLimitPosOut, compId, "boss_plc.%d.%c-limit-pos-out", id, axisNames[2]); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pZLimitNegOut, compId, "boss_plc.%d.%c-limit-neg-out", id, axisNames[2]); } // Export optional parameters. if(debug > 0){ if(!error){ error = hal_param_u32_newf(HAL_RO, &this->xLimit.state, compId, "boss_plc.%d.%c-limit-state", id, axisNames[0]); } if(!error){ error = hal_param_u32_newf(HAL_RO, &this->yLimit.state, compId, "boss_plc.%d.%c-limit-state", id, axisNames[1]); } } return(error); } static int Plc_ExportAmps(Plc *this, int compId, int id, char *name) { int error, i; Amp *pAmp; error = hal_pin_u32_newf(HAL_IO, &this->ampReadyDelay, compId, "boss_plc.%d.amp-ready-delay", id); pAmp = this->amps; for(i = 0; i < NUM_AXIS && !error; i++, pAmp++){ error = Amp_Export(pAmp, compId, id, name, axisNames[i]); } return(error); } static int Plc_ExportSpindle(Plc *this, int compId, int id, char *name) { int error; // Export parameters. error = hal_pin_u32_newf(HAL_IO, &this->brakeOnDelay, compId, "boss_plc.%d.brake-on-delay", id); if(!error){ error = hal_pin_u32_newf(HAL_IO, &this->brakeOffDelay, compId, "boss_plc.%d.brake-off-delay", id); } if(!error){ error = hal_pin_float_newf(HAL_IO, &this->spindleLoToHi, compId, "boss_plc.%d.spindle-lo-to-hi", id); } // Export optional parameters. if(debug > 0){ if(!error){ error = hal_param_u32_newf(HAL_RO, &this->spindleState, compId, "boss_plc.%d.spindle-state", id); } } // Export pins. if(!error){ error = hal_pin_float_newf(HAL_IN, &this->pSpindleSpeedIn, compId, "boss_plc.%d.spindle-speed-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pSpindleIsOnIn, compId, "boss_plc.%d.spindle-is-on-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pSpindleFwdOut, compId, "boss_plc.%d.spindle-fwd-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pSpindleRevOut, compId, "boss_plc.%d.spindle-rev-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pSpindleIncIn, compId, "boss_plc.%d.spindle-inc-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pSpindleDecIn, compId, "boss_plc.%d.spindle-dec-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pSpindleIncOut, compId, "boss_plc.%d.spindle-inc-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pSpindleDecOut, compId, "boss_plc.%d.spindle-dec-out", id); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pBrakeEnIn, compId, "boss_plc.%d.brake-en-in", id); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pBrakeEnOut, compId, "boss_plc.%d.brake-en-out", id); } return(error); } static int Plc_ExportJog(Plc *this, int compId, int id, char *name) { int error, i; // Export parameters. for(i = 0, error = 0; i < NUM_JOG_SEL && !error; i++){ error = hal_pin_float_newf(HAL_IO, &this->jogScale[i], compId, "boss_plc.%d.jog-scale-%d", id, i); } if(!error){ for(i = 0; i < NUM_JOG_SEL && !error; i++){ error = hal_pin_bit_newf(HAL_IN, &this->pJogSelIn[i], compId, "boss_plc.%d.jog-sel-in-%d", id, i); } } // Export pins. if(!error){ error = hal_pin_float_newf(HAL_OUT, &this->pJogScaleOut, compId, "boss_plc.%d.jog-scale-out", id); } return(error); } /* * HAL EXPORTED FUNCTIONS */ static void Plc_Refresh(void *arg, long period) { Plc *this = (Plc *)arg; Plc_RefreshFeed(this, period); Plc_RefreshLimits(this, period); Plc_RefreshAmps(this, period); Plc_RefreshSpindle(this, period); Plc_RefreshJog(this, period); } static void Plc_RefreshFeed(Plc *this, long period) { BOOL riseCycleStart; riseCycleStart = !this->lastCycleStart && *this->pCycleStartIn; this->lastCycleStart = *this->pCycleStartIn; // Condition feed hold so machine waits for cycle start and spindle to be // running if it is enabled. *this->pFeedHoldOut = *this->pCycleHoldIn || (*this->pSpindleSpeedIn && !*this->pSpindleIsOnIn) || (*this->pSpindleIsOnIn && (this->lastSpindleSpeed != *this->pSpindleSpeedIn)) || (*this->pFeedHoldOut && !riseCycleStart); this->lastSpindleSpeed = *this->pSpindleSpeedIn; // Limit rapid/feed to 1% when limits are being overriden. if(*this->pLimitOverrideIn && (*this->pAdaptiveFeedIn > 0.01)) *this->pAdaptiveFeedOut = 0.01; else *this->pAdaptiveFeedOut = *this->pAdaptiveFeedIn; // Wait for cycle start to acknowledge tool change. *this->pToolChangedOut = (*this->pToolChangeIn && riseCycleStart) || (*this->pToolChangedOut && *this->pToolChangeIn); // Indicates waiting for user to press cycle start. *this->pWaitUserOut = *this->pFeedHoldOut || (*this->pToolChangeIn && !*this->pToolChangedOut); // Turn coolant off during tool changes. *this->pMistOnOut = *this->pMistOnIn && !*this->pToolChangeIn; *this->pFloodOnOut = *this->pFloodOnIn && !*this->pToolChangeIn; } static void Plc_RefreshLimits(Plc *this, long period) { Limit_Refresh(&this->xLimit, *this->pLimitOverrideIn); Limit_Refresh(&this->yLimit, *this->pLimitOverrideIn); // Condition Z limits with override in manual mode. *this->pZLimitPosOut = *this->pZLimitPosIn && !(*this->pZJogEnIn && *this->pLimitOverrideIn); *this->pZLimitNegOut = *this->pZLimitNegIn && !(*this->pZJogEnIn && *this->pLimitOverrideIn); // Generate limit active signal for pilot lamp. *this->pLimitActiveOut = Limit_IsActive(&this->xLimit) || Limit_IsActive(&this->yLimit) || *this->pZLimitPosIn || *this->pZLimitNegIn; } static void Plc_RefreshAmps(Plc *this, long period) { int i; Amp *pAmp; pAmp = this->amps; for(i = 0; i < NUM_AXIS; i++, pAmp++){ Amp_Refresh(pAmp, period, *(this->ampReadyDelay)); } } static void Plc_RefreshSpindle(Plc *this, long period) { Timer_Update(&this->spindleTimer, period); switch(this->spindleState){ // Spindle is off, brake is on. case SS_OFF: if(!*this->pBrakeEnIn){ this->spindleState = SS_WAIT_BRAKE_OFF; *this->pBrakeEnOut = 0; Timer_SetTimeout(&this->spindleTimer, *(this->brakeOffDelay)); Timer_Enable(&this->spindleTimer, TM_ONE_SHOT); } break; // Spindle is off, brake has been turned off. Wait at least a brake off // delay before turning spindle on. case SS_WAIT_BRAKE_OFF: if(*this->pBrakeEnIn){ this->spindleState = SS_OFF; *this->pBrakeEnOut = 1; Timer_Disable(&this->spindleTimer); }else if((*this->pSpindleSpeedIn != 0.0) && !Timer_IsEnabled(&this->spindleTimer)){ this->spindleState = SS_WAIT_ON; if(*this->pSpindleSpeedIn > *(this->spindleLoToHi) || (*this->pSpindleSpeedIn < 0.0 && *this->pSpindleSpeedIn >= -*(this->spindleLoToHi))){ *this->pSpindleFwdOut = 1; }else{ *this->pSpindleRevOut = 1; } } break; // Spindle has been turned on. Wait for confirmation that it is running. case SS_WAIT_ON: if(*this->pSpindleIsOnIn){ this->spindleState = SS_ON; }else if(*this->pSpindleSpeedIn == 0.0){ this->spindleState = SS_WAIT_BRAKE_OFF; *this->pSpindleFwdOut = 0; *this->pSpindleRevOut = 0; } break; // Spindle is running. case SS_ON: if(*this->pSpindleSpeedIn == 0.0){ this->spindleState = SS_WAIT_OFF; *this->pSpindleFwdOut = 0; *this->pSpindleRevOut = 0; } break; // Spindle has been turned off. Wait for confirmation. case SS_WAIT_OFF: if(!*this->pSpindleIsOnIn){ this->spindleState = SS_WAIT_BRAKE_ON; Timer_SetTimeout(&this->spindleTimer, *(this->brakeOnDelay)); Timer_Enable(&this->spindleTimer, TM_ONE_SHOT); } break; case SS_WAIT_BRAKE_ON: if(!Timer_IsEnabled(&this->spindleTimer)){ this->spindleState = SS_WAIT_BRAKE_OFF; } break; default: this->spindleState = SS_WAIT_OFF; *this->pSpindleFwdOut = 0; *this->pSpindleRevOut = 0; } // Condition spindle increase and decrease so they are disabled when // spindle is not running and both cannot be enabled at the same time. *this->pSpindleIncOut = *this->pSpindleIncIn && !*this->pSpindleDecIn && *this->pSpindleIsOnIn; *this->pSpindleDecOut = *this->pSpindleDecIn && !*this->pSpindleIncIn && *this->pSpindleIsOnIn; } static void Plc_RefreshJog(Plc *this, long period) { int i; // Jog scale. for(i = 0; i < NUM_JOG_SEL; i++){ if(*this->pJogSelIn[i]){ *this->pJogScaleOut = *(this->jogScale[i]); break; } } } /****************************************************************************** * LIMIT OBJECT FUNCTION DEFINITIONS ******************************************************************************/ static int Limit_Export(Limit *this, int compId, int id, char *name, char axis) { int error; error = hal_pin_float_newf(HAL_IN, &this->pPositionIn, compId, "boss_plc.%d.%c-position-in", id, axis); if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pJogEnIn, compId, "boss_plc.%d.%c-jog-en-in", id, axis); } if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pIn, compId, "boss_plc.%d.%c-limit-in", id, axis); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pPosOut, compId, "boss_plc.%d.%c-limit-pos-out", id, axis); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pNegOut, compId, "boss_plc.%d.%c-limit-neg-out", id, axis); } return(error); } static void Limit_Init(Limit *this) { this->state = LS_INIT; } static BOOL Limit_IsActive(Limit *this) { return(*this->pIn); } static void Limit_Refresh(Limit *this, hal_bit_t override) { switch(this->state){ case LS_INIT: default: this->state = LS_ON_LIMIT; this->limitNeg = this->limitPos = 1; this->position = *this->pPositionIn; // Fall through. case LS_ON_LIMIT: if(*this->pIn == 0){ this->limitNeg = this->limitPos = 0; if(*this->pPositionIn == this->position) this->state = LS_NO_MOTION; else if(*this->pPositionIn > this->position) this->state = LS_POS_MOTION; else if(*this->pPositionIn < this->position) this->state = LS_NEG_MOTION; } break; case LS_NO_MOTION: if(*this->pIn){ this->state = LS_ON_LIMIT; this->limitNeg = this->limitPos = 1; }else if(*this->pPositionIn > this->position){ this->state = LS_POS_MOTION; }else if(*this->pPositionIn < this->position){ this->state = LS_NEG_MOTION; } break; case LS_POS_MOTION: if(*this->pIn){ this->state = LS_ON_LIMIT; this->limitPos = 1; }else if(*this->pPositionIn == this->position){ this->state = LS_NO_MOTION; }else if(*this->pPositionIn < this->position){ this->state = LS_NEG_MOTION; } break; case LS_NEG_MOTION: if(*this->pIn){ this->state = LS_ON_LIMIT; this->limitNeg = 1; }else if(*this->pPositionIn == this->position){ this->state = LS_NO_MOTION; }else if(*this->pPositionIn > this->position){ this->state = LS_POS_MOTION; } break; } this->position = *this->pPositionIn; // Condition limits with override in manual mode. *this->pPosOut = this->limitPos && !(*this->pJogEnIn && override); *this->pNegOut = this->limitNeg && !(*this->pJogEnIn && override); } /****************************************************************************** * AMP OBJECT FUNCTION DEFINITIONS ******************************************************************************/ static int Amp_Export(Amp *this, int compId, int id, char *name, char axis) { int error; error = hal_pin_bit_newf(HAL_IN, &this->pEnableIn, compId, "boss_plc.%d.%c-amp-enable-in", id, axis); if(!error){ error = hal_pin_bit_newf(HAL_IN, &this->pReadyIn, compId, "boss_plc.%d.%c-amp-ready-in", id, axis); } if(!error){ error = hal_pin_bit_newf(HAL_OUT, &this->pFaultOut, compId, "boss_plc.%d.%c-amp-fault-out", id, axis); } return(error); } static void Amp_Init(Amp *this) { // Initialize variables. this->lastEnable = 0; // Initialize timer. Timer_Init(&this->timer); } static void Amp_Refresh(Amp *this, long period, hal_u32_t readyDelay) { Timer_Update(&this->timer, period); if(*this->pEnableIn){ if(!this->lastEnable){ Timer_SetTimeout(&this->timer, readyDelay); Timer_Enable(&this->timer, TM_ONE_SHOT); } } else { Timer_Disable(&this->timer); } *this->pFaultOut = *this->pEnableIn && !*this->pReadyIn && !Timer_IsEnabled(&this->timer); this->lastEnable = *this->pEnableIn; } /****************************************************************************** * TIMER OBJECT FUNCTION DEFINITIONS ******************************************************************************/ static void Timer_Init(Timer *this) { this->enabled = FALSE; this->pTimeout = NULL; } static void Timer_Enable(Timer *this, TimerMode mode) { this->mode = mode; this->enabled = TRUE; this->count = 0; } static void Timer_Disable(Timer *this) { this->enabled = FALSE; } static BOOL Timer_IsEnabled(Timer *this) { return(this->enabled); } static void Timer_Update(Timer *this, long period) { if(!this->enabled) return; this->nSec += period; if(this->nSec > 1000000){ this->count += this->nSec / 1000000; this->nSec %= 1000000; } if(this->count >= this->timeout){ if(this->pTimeout != NULL) this->pTimeout(this->pArgs); if(this->mode != TM_CONTINUOUS){ this->enabled = FALSE; }else{ this->count -= this->timeout; } } } static void Timer_SetTimeout(Timer *this, hal_u32_t timeout) { this->count = 0; this->timeout = timeout; } #if 0 static void Timer_SetCallback(Timer *this, TIMER_ROUTINE pCallback, void *pArgs) { this->pTimeout = pCallback; this->pArgs = pArgs; } #endif