diff --git a/docs/src/code/code-notes.adoc b/docs/src/code/code-notes.adoc index 0b12d11b407..c0f777a390f 100644 --- a/docs/src/code/code-notes.adoc +++ b/docs/src/code/code-notes.adoc @@ -151,6 +151,8 @@ or command line options. Custom modules must implement all functions used by the default modules. The halcompile utility can be used to create a custom module. +The interface a homing module must implement is declared and documented function by function in `src/emc/motion/homing.h`. It comprises one-time initialization (`homing_init`), per-servo-period control (`read_homing_in_pins`, `do_homing`, `write_homing_out_pins`), command entry points (`do_home_joint`, `do_cancel_homing`, `set_unhomed`, and the homing parameter setters), status queries (`get_allhomed`, `get_homed`, `get_homing`, and friends), and a callback registration for the rotary unlock functions (`homeMotFunctions`). The `homecomp` component (`src/hal/components/homecomp.comp`) is a buildable template implementing the full interface; see the homecomp(9) man page. + image::LinuxCNC-motion-controller-small.png[align="center",pdfwidth=100%] == Block diagrams and Data Flow diff --git a/docs/src/config/ini-config.adoc b/docs/src/config/ini-config.adoc index 1ad65f11fa8..5671cea61c2 100644 --- a/docs/src/config/ini-config.adoc +++ b/docs/src/config/ini-config.adoc @@ -686,6 +686,7 @@ For more information on the motion controller see the <> sect * `COMM_TIMEOUT = 1.0` - Number of seconds to wait for Motion (the realtime part of the motion controller) to acknowledge receipt of messages from Task (the non-realtime part of the motion controller). * `HOMEMOD =` _alternate_homing_module_ [home_parms=value] The HOMEMOD variable is optional. If specified, use a specified (user-built) module instead of the default (homemod). + The homecomp(9) component provides a buildable template for custom homing modules. Module parameters (home_parms) may be included if supported by the named module. The setting may be overridden from the command line using the -m option ($ linuxcnc -h). diff --git a/src/emc/motion/homing.h b/src/emc/motion/homing.h index 0c4869de6dd..2b84428195c 100644 --- a/src/emc/motion/homing.h +++ b/src/emc/motion/homing.h @@ -15,8 +15,17 @@ //--------------------------------------------------------------------- // INTERFACE routines +// +// A homing module (default: homemod) provides all functions declared +// below. motmod resolves the symbols at load time, so a custom module +// named by [EMCMOT]HOMEMOD must export every one of them. The +// homecomp component (src/hal/components/homecomp.comp) is a buildable +// template implementing this interface. // per-joint interface parameters (one-time setup) +// Called once per joint with the homing values from the INI file +// [JOINT_N] section (HOME, HOME_OFFSET, velocities, HOME_FLAGS, +// HOME_SEQUENCE, VOLATILE_HOME). void set_joint_homing_params(int jno, double offset, double home, @@ -29,6 +38,8 @@ void set_joint_homing_params(int jno, ); // updateable interface params (for inihal pin changes typically): +// Runtime update of the subset of homing parameters that may change +// after setup (offset, home position, sequence). void update_joint_homing_params (int jno, double home_offset, double home_home, @@ -39,6 +50,10 @@ void update_joint_homing_params (int jno, // CONTROL routines // one-time initialization (return 0 if ok): +// Called from motmod's rtapi_app_main(). 'id' is motmod's HAL +// component id, so pins created here are owned by motmod. 'pjoints' +// points at motmod's joint array. The module creates its HAL pins +// (joint.N.home-sw-in, joint.N.homed, ...) here. int homing_init(int id, double servo_period, int n_joints, // total no of joints @@ -47,36 +62,66 @@ int homing_init(int id, ); // once-per-servo-period functions: +// read_homing_in_pins(): called at the start of every servo period; +// latch HAL input pins (home switches, index-enable, custom inputs). void read_homing_in_pins(int njoints); +// do_homing(): called every servo period while motion is in FREE +// mode; advances the homing sequence and the per-joint state +// machines. Returns 1 on the transition to all-homed so motmod can +// switch from FREE to teleop mode. bool do_homing(void); //return 1 if allhomed +// write_homing_out_pins(): called at the end of every servo period; +// push internal state to HAL output pins (homed, homing, home-state). void write_homing_out_pins(int njoints); // responses to EMCMOT_JOINT_HOME message: +// jno == -1 requests home-all (start the homing sequence). void do_home_joint(int jno); // per-joint controls +// Abort an in-progress homing of joint jno. void do_cancel_homing(int jno); +// Mark joint(s) unhomed. jno == -1 unhomes all joints, jno == -2 +// unhomes joints with VOLATILE_HOME set. motstate guards against +// unhoming an extrajoint while motion is enabled. void set_unhomed(int jno,motion_state_t motstate); //--------------------------------------------------------------------- // QUERIES // overall status: +// get_allhomed(): true if every active joint is homed. bool get_allhomed(void); +// get_homing_is_active(): true while any homing sequence or per-joint +// homing state machine is in progress. bool get_homing_is_active(void); // per-joint information: int get_home_sequence(int jno); //return s +// get_homing(): joint jno is currently running its homing state machine. bool get_homing(int jno); +// get_homed(): joint jno has completed homing. bool get_homed(int jno); +// get_index_enable(): state of the index-enable handshake for joint +// jno (set by the homing module, cleared by the encoder driver). bool get_index_enable(int jno); +// get_home_needs_unlock_first(): joint jno has HOME_UNLOCK_FIRST set +// (rotary axis must be unlocked before homing). bool get_home_needs_unlock_first(int jno); +// get_home_is_idle(): joint jno's homing state machine is HOME_IDLE. bool get_home_is_idle(int jno); +// get_home_is_synchronized(): joint jno homes synchronized with other +// joints (shares a negative home_sequence). bool get_home_is_synchronized(int jno); +// get_homing_at_index_search_wait(): joint jno's state machine waits +// at HOME_INDEX_SEARCH_WAIT (used for index handling). bool get_homing_at_index_search_wait(int jno); //--------------------------------------------------------------------- // Module interface // motmod provided ptrs for functions called by homing: +// Called once by motmod before homing_init() to hand the homing +// module the rotary unlock/lock callbacks, so the module can unlock +// rotaries without including motion internals. void homeMotFunctions(void(*pSetRotaryUnlock)(int,int) ,int( *pGetRotaryUnlock)(int) ); diff --git a/src/hal/components/Submakefile b/src/hal/components/Submakefile index 147517d59b9..d00728e0487 100644 --- a/src/hal/components/Submakefile +++ b/src/hal/components/Submakefile @@ -1,5 +1,5 @@ ifneq ($(KERNELRELEASE),) -COMPS := $(filter-out %/tpcomp.comp %/homecomp.comp, $(patsubst $(BASEPWD)/%,%,$(wildcard $(BASEPWD)/hal/components/*.comp $(BASEPWD)/hal/drivers/*.comp))) +COMPS := $(filter-out %/tpcomp.comp, $(patsubst $(BASEPWD)/%,%,$(wildcard $(BASEPWD)/hal/components/*.comp $(BASEPWD)/hal/drivers/*.comp))) include $(patsubst %.comp, $(BASEPWD)/objects/%.mak, $(COMPS)) else CONVERTERS := \ @@ -32,8 +32,8 @@ CONVERTERS := \ conv_u64_s32.comp \ conv_u64_u32.comp \ conv_u64_s64.comp -COMPS := $(filter-out hal/components/tpcomp.comp hal/components/homecomp.comp, $(sort $(wildcard hal/components/*.comp) $(addprefix hal/components/, $(CONVERTERS)))) -COMP_MANPAGES := $(patsubst hal/components/%.comp, ../docs/build/man/man9/%.9, $(COMPS)) ../docs/build/man/man9/tpcomp.9 ../docs/build/man/man9/homecomp.9 +COMPS := $(filter-out hal/components/tpcomp.comp, $(sort $(wildcard hal/components/*.comp) $(addprefix hal/components/, $(CONVERTERS)))) +COMP_MANPAGES := $(patsubst hal/components/%.comp, ../docs/build/man/man9/%.9, $(COMPS)) ../docs/build/man/man9/tpcomp.9 ifeq ($(BUILD_SYS),uspace) COMP_DRIVERS += hal/drivers/serport.comp COMP_DRIVERS += hal/drivers/mesa_7i65.comp @@ -56,7 +56,7 @@ endif # wildcard that mixes hal/components and hal/drivers, so deriving the adoc # targets from it there yields hal/drivers/*.comp entries that fail the # hal/components/%.comp static pattern rule. -COMP_MANPAGE_ADOCS := $(patsubst hal/components/%.comp, objects/man/man9/%.9.adoc, $(COMPS)) objects/man/man9/tpcomp.9.adoc objects/man/man9/homecomp.9.adoc +COMP_MANPAGE_ADOCS := $(patsubst hal/components/%.comp, objects/man/man9/%.9.adoc, $(COMPS)) objects/man/man9/tpcomp.9.adoc COMP_DRIVER_MANPAGE_ADOCS := $(patsubst hal/drivers/%.comp, objects/man/man9/%.9.adoc, $(COMP_DRIVERS)) # Extract adoc from .comp via halcompile --adoc. Only needs Python + diff --git a/src/hal/components/homecomp.comp b/src/hal/components/homecomp.comp index 8be998239b2..4c616cc76e7 100644 --- a/src/hal/components/homecomp.comp +++ b/src/hal/components/homecomp.comp @@ -1,43 +1,51 @@ -component homecomp"homing module template"; +component homecomp "homing module template"; description """ -Example of a homing module buildable with halcompile. -Demonstrates required code for #includes, function definitions, etc. - -If *HOMING_BASE* is #defined and points to a valid `homing.c` file, -an example of a customized homing module is built. This module -creates input hal pins joint.n.request-custom-homing that enable an -alternate joint homing state machine for requested joints. A hal output -pin joint.N.is_custom-homing verifies selection" - -The customized homing module utilizes many of the base homing api -routines from homing.c without modification but augments other base -functions to add support for custom hal pins and custom joint homing -state machines. A user-built module will likely replace additional -api functions or augment them with other customizations. - -If *HOMING_BASE* is not #defined, an actual homing scheme is -*not* implemented but all necessary functions are included as -skeleton code. (All joints are effectively homed at all times and -cannot be unhomed). - -See the source code file: `src/emc/motion/homing.c` for the baseline -implementation that includes all functions for the default *homemod* -module. - -To avoid updates that overwrite homecomp.comp, best practice is -to rename the file and its component name (example: -*user_homecomp.comp*, *user_homecomp*). - -The (renamed) component can be built and installed with -halcompile and then substituted for the default homing module -(*homemod*) using: +Example of a plug-in homing module buildable with halcompile. +Demonstrates the required code structure for #includes, function +definitions, HAL pins, etc. + +The complete homing module interface (every function a homing module +must export, when motmod calls it, and what it should do) is +documented in `src/emc/motion/homing.h`. + +Build modes: + +* *Skeleton* (default). If HOMING_BASE is _not_ #defined, the module + implements the full homing API but runs no real homing state + machine. A hal pin `motion.allow-sim-homed` (default true) gates + the simulated homed state: with it set, all joints are immediately + reported as homed, which is useful for bring-up and config testing + on hardware without working home switches. With it cleared, each + joint reports homed only while its `joint.N.home-sw-in` pin is + true, showing how HAL inputs feed the homing API. + +* *Custom* (advanced). If HOMING_BASE is #defined to the absolute + path of a checkout's `src/emc/motion/homing.c`, this component + #includes that file with CUSTOM_HOMEMODULE defined. This brings in + the static base_*() helpers from homing.c so user code can call + them or override the per-joint state machine. This is the same + pattern used historically by user_homecomp templates; it requires a + full LinuxCNC source checkout because homing.c is not installed. + +The custom module is an example only: it adds per-joint hal pins +`joint.N.request-custom-homing` and `joint.N.is-custom-homing` so the +operator can pick which joints get the custom path. Requested joints +are simply marked homed immediately; a real custom module would run +its own state machine here instead of calling +base_1joint_state_machine(). + +To avoid updates that overwrite this template, best practice is to +rename the file and its component name (example: *user_homecomp.comp*, +*user_homecomp*). + +The renamed component can be built and installed with halcompile and +then substituted for the default homing module (*homemod*) using: $ linuxcnc -m user_homecomp someconfig.ini or by inifile setting: - [source,ini] ---- [EMCMOT] @@ -46,12 +54,13 @@ HOMEMOD=user_homecomp *Note*: If using a deb install: -1. halcompile is provided by the package linuxcnc-dev\n -2. This source file for BRANCHNAME (master,2.9,etc) is downloadable from github: +1. halcompile is provided by the package linuxcnc-dev + +2. This source file for BRANCHNAME (master, 2.9, etc.) is downloadable + from github: https://github.com/LinuxCNC/linuxcnc/blob/BRANCHNAME/src/hal/components/homecomp.comp """; -pin out bool is_module=1; //one pin is required to use halcompile) +pin out bool is_module = 1; // one pin is required to use halcompile license "GPL"; author "Dewey Garrett"; @@ -59,408 +68,336 @@ option homemod; option extra_setup; ;; -/* To incorporate default homing.c file from a local git src tree: -** enable #define HOMING_BASE set to the path to the current homing.c file. -** (Note: CUSTOM_HOMEMODULE precludes duplicate api symbols) -** (Edit myname as required for valid path) -*/ - -// #define HOMING_BASE /home/myname/linuxcnc-dev/src/emc/motion/homing.c - -#define STR(s) #s +#define STR(s) #s #define XSTR(s) STR(s) -#include "motion.h" -#include "homing.h" +// Fail fast on pin-creation errors: summing return values hides which +// call failed and mangles the error code. +#define CHK(x) \ + do { \ + int _rv = (x); \ + if (0 != _rv) return _rv; \ + } while (0) + +// To build the custom variant, uncomment and point at a checkout: +// #define HOMING_BASE /home/myname/linuxcnc-dev/src/emc/motion/homing.c +// ---- module parameter (homemod forwards to user module) ------------- static char *home_parms; -RTAPI_MP_STRING(home_parms,"Example home parms"); +RTAPI_MP_STRING(home_parms, "Example home parms"); -// EXTRA_SETUP is executed before rtapi_app_main() +// EXTRA_SETUP runs after hal_init(), before motmod resolves symbols. +// The default for allow_sim_homed is set to 1 in mkp() below so the +// skeleton is truly "always homed" out of the box. EXTRA_SETUP() { (void)__comp_inst; (void)prefix; (void)extra_arg; - if (!home_parms) {home_parms = "no_home_parms";} - rtapi_print("@@@%s:%s: home_parms=%s\n",__FILE__,__FUNCTION__,home_parms); + if (!home_parms) home_parms = "no_home_parms"; + rtapi_print("homecomp: %s: home_parms=%s\n", __FUNCTION__, home_parms); #ifndef HOMING_BASE - rtapi_print("\n!!!%s: Skeleton Homing Module\n\n",__FILE__); + rtapi_print("homecomp: skeleton mode (always homed unless " + "motion.allow-sim-homed = 0)\n"); #else - rtapi_print("\n!!!%s: HOMING_BASE=%s\n" - "!!!Customize using hal pin(s): joint.N.request-custom-homing\n" - ,__FILE__,XSTR(HOMING_BASE)); + rtapi_print("homecomp: custom mode, HOMING_BASE=%s\n", + XSTR(HOMING_BASE)); #endif - return 0; } //===================================================================== -#ifdef HOMING_BASE // { begin CUSTOM example -#define USE_HOMING_BASE XSTR(HOMING_BASE) - -// NOTE: CUSTOM_HOMEMODULE: disables duplicate symbols sourced from homing.c -#define CUSTOM_HOMEMODULE -#include USE_HOMING_BASE +// SKELETON: minimal API implementation, no real homing. +// With allow_sim_homed set, all queries report homed. With it +// cleared, get_homed() follows the per-joint home_sw pin to show how +// HAL inputs wire into the get_* helpers. +// +// The skeleton uses opaque pointer and integer arguments so it does +// not depend on motion.h. +//===================================================================== +#ifndef HOMING_BASE +// ---- example: per-joint hal-pin block ------------------------------- typedef struct { - bool request_custom_homing; - bool is_custom_homing; -} custom_home_local_data; - -static custom_home_local_data customH[EMCMOT_MAX_JOINTS]; + hal_bool_t home_sw; // IN : switch state to homing FSM + hal_bool_t homing; // OUT : homing in progress + hal_bool_t homed; // OUT : joint is homed + hal_sint_t home_state; // OUT : state machine state (enum) + hal_bool_t index_enable; // IO : motmod sets / encoder clears +} one_joint_home_data_t; -// data for per-joint custom-homing-specific hal pins: +// All pin storage must live in HAL shared memory, so everything goes +// in a single hal_malloc'd block sized from n_joints at runtime (see +// homing_init); the template does not depend on EMCMOT_MAX_JOINTS. typedef struct { - hal_bool_t request_custom_homing; // input requests custom homing - hal_bool_t is_custom_homing; // output verifies custom homing -} custom_one_joint_home_data_t; + hal_bool_t allow_sim_homed; // HAL_IO: motion.allow-sim-homed + one_joint_home_data_t jhd[]; // n_joints entries +} home_data_t; -typedef struct { - custom_one_joint_home_data_t custom_jhd[EMCMOT_MAX_JOINTS]; -} custom_all_joints_home_data_t; +static home_data_t *hd = 0; +static int g_njoints; // saved count -static custom_all_joints_home_data_t *custom_joint_home_data = 0; +#define joint_home_data (hd->jhd) +#define allow_sim_homed_pin (hd->allow_sim_homed) -static int custom_makepins(int id,int njoints) +// Create the per-joint HAL pins plus a single module-level pin. +static int mkp(int id, int njoints) { - int jno,retval; - custom_one_joint_home_data_t *addr; + int jno; + one_joint_home_data_t *p; - custom_joint_home_data = hal_malloc(sizeof(*custom_joint_home_data)); - if (custom_joint_home_data == 0) { - rtapi_print_msg(RTAPI_MSG_ERR, "HOMING: custom_all_joints_home_data_t malloc failed\n"); + hd = hal_malloc(sizeof(home_data_t) + + njoints * sizeof(one_joint_home_data_t)); + if (hd == 0) { + rtapi_print_msg(RTAPI_MSG_ERR, + "homecomp: home_data_t malloc failed\n"); return -1; } - retval = 0; - for (jno = 0; jno < njoints; jno++) { - addr = &(custom_joint_home_data->custom_jhd[jno]); + CHK(hal_pin_new_bool(id, HAL_IO, &allow_sim_homed_pin, 1, + "motion.allow-sim-homed")); - retval += hal_pin_new_bool(id, HAL_IN, &(addr->request_custom_homing), 0, - "joint.%d.request-custom-homing", jno); - retval += hal_pin_new_bool(id, HAL_OUT, &(addr->is_custom_homing), 0, - "joint.%d.is-custom-homing", jno); + for (jno = 0; jno < njoints; jno++) { + p = &joint_home_data[jno]; + CHK(hal_pin_new_bool(id, HAL_IN, &p->home_sw, 0, + "joint.%d.home-sw-in", jno)); + CHK(hal_pin_new_bool(id, HAL_OUT, &p->homing, 0, + "joint.%d.homing", jno)); + CHK(hal_pin_new_bool(id, HAL_OUT, &p->homed, 0, + "joint.%d.homed", jno)); + CHK(hal_pin_new_si32(id, HAL_OUT, &p->home_state, 0, + "joint.%d.home-state", jno)); + CHK(hal_pin_new_bool(id, HAL_IO, &p->index_enable, 0, + "joint.%d.index-enable", jno)); } - return retval; -} // custom_makepins() + return 0; +} -static void custom_read_homing_in_pins(int njoints) +bool get_homed(int jno) { - int jno; - custom_one_joint_home_data_t *addr; - for (jno = 0; jno < njoints; jno++) { - addr = &(custom_joint_home_data->custom_jhd[jno]); - customH[jno].request_custom_homing = hal_get_bool(addr->request_custom_homing); // IN + if (jno < 0 || jno >= g_njoints) return 0; + if (hal_get_bool(allow_sim_homed_pin)) return 1; + return hal_get_bool(joint_home_data[jno].home_sw); +} - // echo for verification: - customH[jno].is_custom_homing = customH[jno].request_custom_homing; - } +// HAL samples IN/IO pins before read_homing_in_pins() runs; the +// skeleton's get_*() helpers read the pin storage directly, so there +// is nothing to latch here. +static void rd(int njoints) +{ + (void)njoints; } -static void custom_write_homing_out_pins(int njoints) +// Mirror the internal homed state onto the HAL output pins once per +// servo period so GUIs and hal meters see what motion believes. +static void wr(int njoints) { int jno; - custom_one_joint_home_data_t *addr; for (jno = 0; jno < njoints; jno++) { - addr = &(custom_joint_home_data->custom_jhd[jno]); - hal_set_bool(addr->is_custom_homing, customH[jno].is_custom_homing); // OUT + one_joint_home_data_t *p = &joint_home_data[jno]; + hal_set_bool(p->homing, 0); + hal_set_bool(p->homed, get_homed(jno)); + hal_set_si32(p->home_state, 0); // HOME_IDLE } } -static int custom_1joint_state_machine(int joint_num) -{ -typedef enum { - CUSTOM_IDLE = 0, - CUSTOM_1 = 1, - CUSTOM_2 = 2, - CUSTOM_3 = 3, - CUSTOM_4 = 4, - CUSTOM_FINI = 5, -} custom_home_state_t; - -static custom_home_state_t chomestate[EMCMOT_MAX_JOINTS] = {0}; - custom_home_state_t nextcstate; - -#define C_SHOW \ - rtapi_print("H[%d].homed=%d,homing=%d,home_state=%d chomestate[%d]=%d next=%d\n" \ - ,joint_num,H[joint_num].homed,H[joint_num].homing,H[joint_num].home_state \ - ,joint_num,chomestate[joint_num],nextcstate); - - if ( H[joint_num].home_state == HOME_IDLE) return 0; // nothing to do - - if ((H[joint_num].home_state == HOME_START) && (chomestate[joint_num] == CUSTOM_IDLE) ) { - H[joint_num].homing = 1; - H[joint_num].homed = 0; - chomestate[joint_num] = CUSTOM_1; // set first non-idle custom_home_state - } - // For this example, just walk thru custom_home_states with prints. - // Note: remains in the base home_state: HOME_START for all custom_home_states. - // On completion, return to HOME_IDLE, CUSTOM_IDLE states. - switch (chomestate[joint_num]) { - case CUSTOM_1: - // Each CUSTOM_* state should do something and/or check something - // and set nexstcstate according to the design goals. - // Halpin variables can be read and/or set for next write. - nextcstate=CUSTOM_2; C_SHOW; chomestate[joint_num] = nextcstate; break; - case CUSTOM_2: nextcstate=CUSTOM_3; C_SHOW; chomestate[joint_num] = nextcstate; break; - case CUSTOM_3: nextcstate=CUSTOM_4; C_SHOW; chomestate[joint_num] = nextcstate; break; - case CUSTOM_4: nextcstate=CUSTOM_FINI; C_SHOW; chomestate[joint_num] = nextcstate; break; - case CUSTOM_FINI: - H[joint_num].homing = 0; - H[joint_num].homed = 1; - H[joint_num].home_state = HOME_IDLE; - nextcstate = CUSTOM_IDLE; - C_SHOW; - chomestate[joint_num] = nextcstate; - return 0; // finished custom_home_states - break; - case CUSTOM_IDLE: - default: rtapi_print("Unhandled custom_home_state: %d\n",chomestate[joint_num]); - } - return 1; // return 1 if busy -#undef C_SHOW -} // custom_1joint_state_machine() - -// api functions below augment base_*() functions with custom code -int homing_init(int id, - double servo_period, - int n_joints, - int n_extrajoints, - emcmot_joint_t* pjoints) +int homing_init(int id, double servo_period, int n_joints, + int n_extrajoints, void *pjoints) { - int retval; - retval = base_homing_init(id, - servo_period, - n_joints, - n_extrajoints, - pjoints); - retval += custom_makepins(id,n_joints); - return retval; -} // homing_init() + (void)servo_period; (void)n_extrajoints; (void)pjoints; + g_njoints = n_joints; + return mkp(id, n_joints); +} -void read_homing_in_pins(int njoints) +void homeMotFunctions(void (*pSet)(int,int), int (*pGet)(int)) { - base_read_homing_in_pins(njoints); - custom_read_homing_in_pins(njoints); + (void)pSet; (void)pGet; } -void write_homing_out_pins(int njoints) +bool get_allhomed(void) { - base_write_homing_out_pins(njoints); - custom_write_homing_out_pins(njoints); + int jno; + for (jno = 0; jno < g_njoints; jno++) { + if (!get_homed(jno)) return 0; + } + return 1; } -/* do_homing() is adapted from homing.c:base_do_homing() augmented -** with support for custom homing as specified on hal input pin: -** joint.n.request-custom-homing and echoed on hal output pin -** joint.n.is-custom-homing -*/ +// Return 1 on the not-allhomed => allhomed transition so motmod +// switches from FREE to teleop mode, matching base_do_homing(). bool do_homing(void) { - int joint_num; - int homing_flag = 0; - bool beginning_allhomed = get_allhomed(); - - do_homing_sequence(); - /* loop thru joints, treat each one individually */ - for (joint_num = 0; joint_num < all_joints; joint_num++) { - if (!H[joint_num].joint_in_sequence) { continue; } - if (!GET_JOINT_ACTIVE_FLAG(&joints[joint_num])) { continue; } - - if (customH[joint_num].is_custom_homing) { - // CUSTOM joint homing state machine: - homing_flag += custom_1joint_state_machine(joint_num); - } else { - // DEFAULT joint homing state machine: - homing_flag += base_1joint_state_machine(joint_num); - } - } - if ( homing_flag > 0 ) { /* one or more joint is homing */ - homing_active = 1; - } else { /* is a homing sequence in progress? */ - if (sequence_state == HOME_SEQUENCE_IDLE) { - /* no, single joint only, we're done */ - homing_active = 0; - } - } - // return 1 if homing completed this period - if (!beginning_allhomed && get_allhomed()) {homing_active=0; return 1;} - return 0; + static bool was_allhomed; + bool now = get_allhomed(); + bool completed = !was_allhomed && now; + was_allhomed = now; + return completed; } -//=============================================================================== -// functions below use unmodified base_*() implementation -bool get_allhomed(void) { return base_get_allhomed(); } -bool get_homed(int jno) { return base_get_homed(jno); } -bool get_home_is_idle(int jno) { return base_get_home_is_idle(jno); } -bool get_home_is_synchronized(int jno) { return base_get_home_is_synchronized(jno); } -bool get_home_needs_unlock_first(int jno) { return base_get_home_needs_unlock_first(jno); } -int get_home_sequence(int jno) { return base_get_home_sequence(jno); } -bool get_homing(int jno) { return base_get_homing(jno); } -bool get_homing_at_index_search_wait(int jno) { return base_get_homing_at_index_search_wait(jno); } -bool get_homing_is_active(void) { return base_get_homing_is_active(); } -bool get_index_enable(int jno) { return base_get_index_enable(jno); } - -void do_home_joint(int jno) { base_do_home_joint(jno); } -void do_cancel_homing(int jno) { base_do_cancel_homing(jno); } -void set_unhomed(int jno, motion_state_t motstate) { base_set_unhomed(jno,motstate); } -void set_joint_homing_params(int jno, - double offset, - double home, +bool get_home_is_idle(int jno) { (void)jno; return 1; } +bool get_home_is_synchronized(int jno) { (void)jno; return 0; } +bool get_home_needs_unlock_first(int jno) { (void)jno; return 0; } +int get_home_sequence(int jno) { (void)jno; return 0; } +bool get_homing(int jno) { (void)jno; return 0; } +bool get_homing_at_index_search_wait(int jno) { (void)jno; return 0; } +bool get_homing_is_active(void) { return 0; } +bool get_index_enable(int jno) { (void)jno; return 0; } + +void read_homing_in_pins(int njoints) { rd(njoints); } +void write_homing_out_pins(int njoints) { wr(njoints); } +void do_home_joint(int jno) { (void)jno; } +void do_cancel_homing(int jno) { (void)jno; } +void set_unhomed(int jno, int ms) { (void)jno; (void)ms; } + +void set_joint_homing_params(int jno, + double offset, double home, double home_final_vel, double home_search_vel, double home_latch_vel, - int home_flags, - int home_sequence, - bool volatile_home - ) + int home_flags, int home_sequence, + bool volatile_home) { - base_set_joint_homing_params(jno, - offset, - home, - home_final_vel, - home_search_vel, - home_latch_vel, - home_flags, - home_sequence, - volatile_home); + (void)jno; (void)offset; (void)home; + (void)home_final_vel; (void)home_search_vel; (void)home_latch_vel; + (void)home_flags; (void)home_sequence; (void)volatile_home; } -void update_joint_homing_params(int jno, - double offset, - double home, - int home_sequence - ) + +void update_joint_homing_params(int jno, double offset, double home, + int home_sequence) { - base_update_joint_homing_params (jno, - offset, - home, - home_sequence - ); + (void)jno; (void)offset; (void)home; (void)home_sequence; } -// end CUSTOM example + +#endif // SKELETON end +//===================================================================== +// CUSTOM: include homing.c from a source checkout, override select +// base_* helpers. Requires HOMING_BASE set to the absolute path of +// the user's checkout of src/emc/motion/homing.c. This block is the +// original homecomp pattern (forum reference in description above). //===================================================================== -#else // } { begin SKELETON example minimal api implementation -static emcmot_joint_t *joints; +#ifdef HOMING_BASE -// data for per-joint homing-specific hal pins: -typedef struct { - hal_bool_t home_sw; // home switch input - hal_bool_t homing; // joint is homing - hal_bool_t homed; // joint was homed - hal_bool_t index_enable; // motmod sets: request reset on index - // encoder clears: index arrived - hal_sint_t home_state; // homing state machine state -} one_joint_home_data_t; +// Note: no #include of motion.h/homing.h here. Those headers are not +// exported to the halcompile include path; homing.c's own quoted +// includes resolve relative to its source directory and pull in +// everything the declarations below need. +#define USE_HOMING_BASE XSTR(HOMING_BASE) +#define CUSTOM_HOMEMODULE // suppress duplicate-symbol exports in homing.c +#include USE_HOMING_BASE +// Per-joint toggle for the custom state machine. typedef struct { - one_joint_home_data_t jhd[EMCMOT_MAX_JOINTS]; -} all_joints_home_data_t; + hal_bool_t request_custom_homing; // IN + hal_bool_t is_custom_homing; // OUT (echo) +} custom_one_joint_home_data_t; -static all_joints_home_data_t *joint_home_data = 0; +static custom_one_joint_home_data_t *custom_data = 0; -static int makepins(int id,int njoints) +static int custom_makepins(int id, int njoints) { - // home_pins needed to work with configs expecting them: - int jno,retval; - one_joint_home_data_t *addr; - - joint_home_data = hal_malloc(sizeof(all_joints_home_data_t)); - if (joint_home_data == 0) { - rtapi_print_msg(RTAPI_MSG_ERR, "HOMING: all_joints_home_data_t malloc failed\n"); + int jno; + custom_data = hal_malloc(sizeof(*custom_data) * njoints); + if (!custom_data) { + rtapi_print_msg(RTAPI_MSG_ERR, + "homecomp: custom_data malloc failed\n"); return -1; } - - retval = 0; for (jno = 0; jno < njoints; jno++) { - addr = &(joint_home_data->jhd[jno]); - - retval += hal_pin_new_bool(id, HAL_IN, &(addr->home_sw), 0, - "joint.%d.home-sw-in", jno); - retval += hal_pin_new_bool(id, HAL_OUT, &(addr->homing), 0, - "joint.%d.homing", jno); - retval += hal_pin_new_bool(id, HAL_OUT, &(addr->homed), 0, - "joint.%d.homed", jno); - retval += hal_pin_new_si32(id, HAL_OUT, &(addr->home_state), 0, - "joint.%d.home-state", jno); - retval += hal_pin_new_bool(id, HAL_IO, &(addr->index_enable), 0, - "joint.%d.index-enable", jno); + CHK(hal_pin_new_bool(id, HAL_IN, + &custom_data[jno].request_custom_homing, 0, + "joint.%d.request-custom-homing", jno)); + CHK(hal_pin_new_bool(id, HAL_OUT, + &custom_data[jno].is_custom_homing, 0, + "joint.%d.is-custom-homing", jno)); } - return retval; + return 0; +} + +int homing_init(int id, double servo_period, int n_joints, + int n_extrajoints, emcmot_joint_t *pjoints) +{ + CHK(base_homing_init(id, servo_period, n_joints, + n_extrajoints, pjoints)); + CHK(custom_makepins(id, n_joints)); + return 0; +} + +void read_homing_in_pins(int njoints) +{ + base_read_homing_in_pins(njoints); + // Sample custom inputs (HAL updates pins, this just records state). } -// All (skeleton) functions required for homing api follow: -void homeMotFunctions(void(*pSetRotaryUnlock)(int,int) - ,int (*pGetRotaryIsUnlocked)(int) - ) + +void write_homing_out_pins(int njoints) { - (void)pSetRotaryUnlock; - (void)pGetRotaryIsUnlocked; - return; + int jno; + base_write_homing_out_pins(njoints); + for (jno = 0; jno < njoints; jno++) { + if (!custom_data) continue; + hal_set_bool(custom_data[jno].is_custom_homing, + hal_get_bool(custom_data[jno].request_custom_homing)); + } } -int homing_init(int id, - double servo_period, - int n_joints, - int n_extrajoints, - emcmot_joint_t* pjoints) { - (void)servo_period; - (void)n_extrajoints; - joints = pjoints; - return makepins(id,n_joints); +bool do_homing(void) +{ + int jno, busy = 0; + bool begin_all = base_get_allhomed(); + (void)base_do_homing; // replaced by this custom do_homing() + do_homing_sequence(); + for (jno = 0; jno < all_joints; jno++) { + if (!H[jno].joint_in_sequence) continue; + if (!GET_JOINT_ACTIVE_FLAG(&joints[jno])) continue; + if (custom_data && hal_get_bool(custom_data[jno].request_custom_homing)) { + // Trivial example: mark requested joints homed at once. + // Real users would run their own state machine here. + H[jno].homed = 1; + H[jno].homing = 0; + H[jno].home_state = HOME_IDLE; + } else { + busy += base_1joint_state_machine(jno); + } + } + if (busy > 0) homing_active = 1; + else if (sequence_state == HOME_SEQUENCE_IDLE) homing_active = 0; + if (!begin_all && base_get_allhomed()) { homing_active = 0; return 1; } + return 0; } -bool do_homing(void) {return 1;} -bool get_allhomed() {return 1;} -bool get_homed(int jno) { (void)jno; return 1;} -bool get_home_is_idle(int jno) { (void)jno; return 1;} -bool get_home_is_synchronized(int jno) { (void)jno; return 0;} -bool get_home_needs_unlock_first(int jno) { (void)jno; return 0;} -int get_home_sequence(int jno) { (void)jno; return 0;} -bool get_homing(int jno) { (void)jno; return 0;} -bool get_homing_at_index_search_wait(int jno) { (void)jno; return 0;} -bool get_homing_is_active() {return 0;} -bool get_index_enable(int jno) { (void)jno; return 0;} -void read_homing_in_pins(int njoints) { (void)njoints; return;} -void do_home_joint(int jno) { (void)jno; return;} -void set_unhomed(int jno,motion_state_t motstate) { (void)jno; (void)motstate; return;} -void do_cancel_homing(int jno) { (void)jno; return;} -void set_joint_homing_params(int jno, - double offset, - double home, + +bool get_allhomed(void) { return base_get_allhomed(); } +bool get_homed(int jno) { return base_get_homed(jno); } +bool get_home_is_idle(int jno) { return base_get_home_is_idle(jno); } +bool get_home_is_synchronized(int jno) { return base_get_home_is_synchronized(jno); } +bool get_home_needs_unlock_first(int jno) { return base_get_home_needs_unlock_first(jno); } +int get_home_sequence(int jno) { return base_get_home_sequence(jno); } +bool get_homing(int jno) { return base_get_homing(jno); } +bool get_homing_at_index_search_wait(int jno) { return base_get_homing_at_index_search_wait(jno); } +bool get_homing_is_active(void) { return base_get_homing_is_active(); } +bool get_index_enable(int jno) { return base_get_index_enable(jno); } + +void do_home_joint(int jno) { base_do_home_joint(jno); } +void do_cancel_homing(int jno) { base_do_cancel_homing(jno); } +void set_unhomed(int jno, motion_state_t ms) { base_set_unhomed(jno, ms); } +void set_joint_homing_params(int jno, + double offset, double home, double home_final_vel, double home_search_vel, double home_latch_vel, - int home_flags, - int home_sequence, - bool volatile_home - ) + int home_flags, int home_sequence, + bool volatile_home) { - (void)jno; - (void)offset; - (void)home; - (void)home_final_vel; - (void)home_search_vel; - (void)home_latch_vel; - (void)home_flags; - (void)home_sequence; - (void)volatile_home; - return; + base_set_joint_homing_params(jno, offset, home, + home_final_vel, home_search_vel, home_latch_vel, + home_flags, home_sequence, volatile_home); } -void update_joint_homing_params (int jno, - double offset, - double home, - int home_sequence - ) +void update_joint_homing_params(int jno, double offset, + double home, int home_sequence) { - (void)jno; - (void)offset; - (void)home; - (void)home_sequence; - return; + base_update_joint_homing_params(jno, offset, home, home_sequence); } -void write_homing_out_pins(int njoints) { (void)njoints; return;} -#endif // } end SKELETON example minimal api implementation + +#endif // CUSTOM end //===================================================================== -// all home functions for homing api EXPORT_SYMBOL(homeMotFunctions); EXPORT_SYMBOL(homing_init); @@ -482,9 +419,3 @@ EXPORT_SYMBOL(set_unhomed); EXPORT_SYMBOL(set_joint_homing_params); EXPORT_SYMBOL(update_joint_homing_params); EXPORT_SYMBOL(write_homing_out_pins); - -#undef XSTR -#undef STR -#undef HOMING_BASE -#undef USE_HOMING_BASE -#undef CUSTOM_HOMEMODULE