Added switch for fixing an unhomed motor
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 5s

Incremental encoders may report a wrong position after a
power cycle until they have been homed. Since the position of the motor is then
unknown, the motor might hit the limit switches when moving. The newly
introduced record $(INSTR)$(M):FixIfNotHomed provides a switch to
prohibit / allow movement of motors with incremental encoder when they
haven't been homed yet.
This commit is contained in:
2026-03-13 10:19:00 +01:00
parent 86e6ab1fdd
commit 8a0c3767ff
5 changed files with 66 additions and 26 deletions
+10 -5
View File
@@ -117,11 +117,11 @@ does not matter):
file "$(SINQDBPATH)"
{
pattern
{ AXIS, M, DESC, EGU, DIR, MRES, ERRORMSGSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED, ADAPTPOLL }
{ 1, "lin1", "Linear motor doing whatever", mm, Pos, 0.001, 200, 1, 1.0, 1, 1 }
{ 2, "rot1", "First rotary motor", degree, Neg, 0.001, 200, 0, 1.0, 0, 1 }
{ 3, "rot2", "Second rotary motor", degree, Pos, 0.001, 200, 0, 0.0, 1, 0 }
{ 5, "rot3", "Surprise: Third rotary motor", degree, Pos, 0.001, 200, 1, 2.0, 0, 0 }
{ AXIS, M, DESC, EGU, DIR, MRES, ERRORMSGSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED, FIXIFNOTHOMED, ADAPTPOLL }
{ 1, "lin1", "Linear motor doing whatever", mm, Pos, 0.001, 200, 1, 1.0, 1, 1, 1 }
{ 2, "rot1", "First rotary motor", degree, Neg, 0.001, 200, 0, 1.0, 0, 1, 1 }
{ 3, "rot2", "Second rotary motor", degree, Pos, 0.001, 200, 0, 0.0, 1, 1, 0 }
{ 5, "rot3", "Surprise: Third rotary motor", degree, Pos, 0.001, 200, 1, 2.0, 0, 1, 0 }
}
```
The variable `SINQDBPATH` has been set in "mcu1.cmd" before calling `dbLoadTemplate`.
@@ -157,6 +157,11 @@ read-out limits are [-10.0 10.0], the EPICS limits are set to [-9.0 9.0]. This
parameter uses engineering units (EGU). Defaults to 0.0.
- `CANSETSPEED`: If set to 1, the motor speed can be modified by the user.
Defaults to 0.
- `FIXIFNOTHOMED`: Incremental encoders may report a wrong position after a
power cycle until they have been homed. Since the position of the motor is then
unknown, the motor might hit the limit switches when moving. If this parameter
is set to 1 (or any other nonzero-value), movement is blocked until the motor
has been homed. Defaults to 0.
- `ADAPTPOLL`: If set to any value other than 0, adaptive polling is enabled for
this particular axis. Adaptive polling is designed to reduce the communication
load in case some axis is moving. By default, if at least one axis is moving,
+17 -2
View File
@@ -150,8 +150,9 @@ record(longin, "$(INSTR)$(M):CanDisable") {
}
# For some motors, the user might be allowed to adjust the speed within the
# limits specified in the motor record as VBAS and VMAX. This functionality can
# be enabled by setting CANSETSPEED to 1. It is disabled by default.
# limits specified in the motor record as VBAS and VMAX. This functionality can
# be enabled by setting CANSETSPEED in the substitution file to 1. It is
# disabled by default.
# This record is coupled to the parameter library via motorCanSetSpeed -> MOTOR_CAN_SET_SPEED.
record(longout, "$(INSTR)$(M):CanSetSpeed") {
field(DTYP, "asynInt32")
@@ -161,6 +162,20 @@ record(longout, "$(INSTR)$(M):CanSetSpeed") {
field(VAL, "$(CANSETSPEED=0)")
}
# If a motor has an incremental encoder, the position value given by the encoder
# might be wrong unless the motor is known to being homed since the last
# controller startup. As a safety feature, movement of the motor is be disabled
# in such a case if this record has a value other than zero. The record value
# can be set either by setting FIXIFNOTHOMED in the substitution file to to 1 or
# by changing the value of this record at runtime. It is disabled by default.
# This record is coupled to the parameter library via motorFixIfNotHomed -> MOTOR_FIX_IF_NOT_HOMED.
record(longout, "$(INSTR)$(M):FixIfNotHomed") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_FIX_IF_NOT_HOMED")
field(PINI, "YES")
field(VAL, "$(FIXIFNOTHOMED=0)")
}
# If this PV has a value other than 0, adaptive polling for this axis is enabled.
# The standard motor record behaviour is to poll all axis with the busy / move poll
# period if at least one of the axes is moving. Adaptive polling changes this so
+24 -19
View File
@@ -369,28 +369,33 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
asynStatus status = asynSuccess;
double motorRecRes = 0.0;
char encType[pC_->MAXBUF_] = {0};
int motorStatHomed = 0;
int motStatusHomed = 0;
int motFixIfNotHomed = 0;
// =========================================================================
/*
Check if the motor is allowed to move: If the motor hasn't been homed in the
past and has an incremental encoder, it needs to be homed first!
*/
getAxisParamChecked(this, encoderType, &encType);
getAxisParamChecked(this, motorStatusHomed, &motorStatHomed);
if (strcmp(encType, IncrementalEncoder) == 0 && motorStatHomed == 0) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nAxis needs to be homed first.\n",
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(this, motorErrorMessage,
"Motor needs to be homed / referenced first.");
setAxisParamChecked(this, motorStatusProblem, true);
epicsTimeStamp ts;
epicsTimeGetCurrent(&ts);
pSinqA_->errorInitialAppearance = ts;
return pC_->callParamCallbacks();
// Check if the motor is set to be fixed if it hasn't been homed
getAxisParamChecked(this, motorFixIfNotHomed, &motFixIfNotHomed);
if (motFixIfNotHomed) {
/*
If the motor hasn't been homed in
the past and has an incremental encoder, it needs to be homed first!
*/
getAxisParamChecked(this, encoderType, &encType);
getAxisParamChecked(this, motorStatusHomed, &motStatusHomed);
if (strcmp(encType, IncrementalEncoder) == 0 && motStatusHomed == 0) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nAxis needs to be homed first.\n",
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(this, motorErrorMessage,
"Motor needs to be homed / referenced first.");
setAxisParamChecked(this, motorStatusProblem, true);
epicsTimeStamp ts;
epicsTimeGetCurrent(&ts);
pSinqA_->errorInitialAppearance = ts;
return pC_->callParamCallbacks();
}
}
// Store the target position internally
+14
View File
@@ -83,6 +83,7 @@ struct sinqControllerImpl {
int motorCanDisable;
int motorEnableMovWatchdog;
int motorCanSetSpeed;
int motorFixIfNotHomed;
int motorLimitsOffset;
int motorForceStop;
int motorConnected;
@@ -137,6 +138,7 @@ sinqController::sinqController(const char *portName,
.motorCanDisable = 0,
.motorEnableMovWatchdog = 0,
.motorCanSetSpeed = 0,
.motorFixIfNotHomed = 0,
.motorLimitsOffset = 0,
.motorForceStop = 0,
.motorConnected = 0,
@@ -243,6 +245,17 @@ sinqController::sinqController(const char *portName,
exit(-1);
}
status = createParam("MOTOR_FIX_IF_NOT_HOMED", asynParamInt32,
&pSinqC_->motorFixIfNotHomed);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_LIMITS_OFFSET", asynParamFloat64,
&pSinqC_->motorLimitsOffset);
if (status != asynSuccess) {
@@ -718,6 +731,7 @@ int sinqController::motorEnableMovWatchdog() {
return pSinqC_->motorEnableMovWatchdog;
}
int sinqController::motorCanSetSpeed() { return pSinqC_->motorCanSetSpeed; }
int sinqController::motorFixIfNotHomed() { return pSinqC_->motorFixIfNotHomed; }
int sinqController::motorLimitsOffset() { return pSinqC_->motorLimitsOffset; }
int sinqController::motorForceStop() { return pSinqC_->motorForceStop; }
int sinqController::motorConnected() { return pSinqC_->motorConnected; }
+1
View File
@@ -355,6 +355,7 @@ class HIDDEN sinqController : public asynMotorController {
int motorCanDisable();
int motorEnableMovWatchdog();
int motorCanSetSpeed();
int motorFixIfNotHomed();
int motorLimitsOffset();
int motorForceStop();
int motorConnected();