rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
servo_limrun.h File Reference

The limit run: the limit search (servo_limit.h) run on the SERVO screen against the INA3221's windows, and for a KST or Chaservo servo on the programming port, the servo's own travel limits lowered until neither end binds. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "servo_limit.h"
#include "servo_test.h"

Go to the source code of this file.

Data Structures

struct  servo_limrun_cfg_t
struct  servo_limrun_servo_t
 The servo the programmer read. More...
struct  servo_limrun_in_t
 What the caller knows each step. More...
struct  servo_limrun_do_t
 What the run wants done. More...
struct  servo_limrun_side_t
 What one search of one side found. More...
struct  servo_limrun_t

Macros

#define SERVO_LIMRUN_WRITES_MAX   8u
 Register values a run writes at most: PULSE LOWER and PULSE UPPER for SERVO PULSES at 1000-2000, two a side, the overset and the final value, and two for a first write that went to the register of the other side, that value and LEFT ANGLE's own value back.
#define SERVO_LIMRUN_OVERSET_PCT   3u
 The overset write lies this far above the share at the first search's first bound pulse: the stop stays in reach, close to the pulse limit.
#define SERVO_LIMRUN_STD_LOWER_DUS   10000u
 The pulses SERVO PULSES at 1000-2000 writes, 0.1us.
#define SERVO_LIMRUN_STD_UPPER_DUS   20000u
#define SERVO_LIMRUN_MOVED_US   20u
 An endpoint that moves this far out moved; one within it stayed.
#define SERVO_LIMRUN_SEARCH_MS   40000u
 One search of one side at most: 1000us of travel, PULSE MIN 500us to a 1500us centre, is 100 positions of 250ms, 25s; the baseline's three and the slower windows of a busy panel fit in the rest.
#define SERVO_LIMRUN_RETURN_MS   1000u
 Each search starts with the servo held at PULSE CENTRE this long, and each session waits this long at PULSE CENTRE before the bench is asked to SAFE: the return from the last end, 600us at 0.67us/ms (0.3s for 60 degrees) is 900ms, and the search's baseline is not to see it travel.
#define SERVO_LIMRUN_PROG_MS   150000u
 One step of the programmer – CONNECT with its read, a write, LEAVE – at most: past the programmer's own 120s of an operation.
#define SERVO_LIMRUN_BENCH_MS   3000u
 The bench switches to SAFE, or to ARMED, within this of being asked.
#define SERVO_LIMRUN_HARD_MA   3000
 The protections: a 1ms sample, and the window means for a time.
#define SERVO_LIMRUN_STALL_MA   1000
#define SERVO_LIMRUN_STALL_MS   400u
#define SERVO_LIMRUN_WIN_STALE_MS   SERVO_TEST_WIN_STALE_MS
 No window for this long ends the run: the automatic test's figure.
#define SERVO_LIMRUN_RAIL_DV_MV   1000
 The rail voltage, a window's mean, this far from the first window's ends the run as a changed supply.
#define SERVO_LIMRUN_RAIL_MIN_MV   3000
 The servo rail a run starts on at least, the last INA3221 window's mean (the screen's START check): an output that is off reads 0V, and a search there finds two free ends on a servo that never moved.
#define SERVO_LIMRUN_OUTBOX   2u

Enumerations

enum  { SERVO_LIMRUN_LO = 0 , SERVO_LIMRUN_HI = 1 }
 The two sides of PULSE CENTRE: shorter and longer pulses.
enum  { SERVO_LIMRUN_LEFT = 0 , SERVO_LIMRUN_RIGHT = 1 }
 The two registers.
enum  { SERVO_LIMRUN_P_NEUTRAL = 0 , SERVO_LIMRUN_P_LOWER = 1 , SERVO_LIMRUN_P_UPPER = 2 }
 The servo's pulses, in pulse_dus.
enum  servo_limrun_state_t { SERVO_LIMRUN_IDLE = 0 , SERVO_LIMRUN_RUNNING , SERVO_LIMRUN_DONE }
enum  servo_limrun_phase_t {
  SERVO_LIMRUN_PH_NONE = 0 , SERVO_LIMRUN_PH_SEARCH , SERVO_LIMRUN_PH_SAFE , SERVO_LIMRUN_PH_CONNECT ,
  SERVO_LIMRUN_PH_WRITE , SERVO_LIMRUN_PH_LEAVE , SERVO_LIMRUN_PH_ARM , SERVO_LIMRUN_PH_PROBE ,
  SERVO_LIMRUN_PH_CHECK
}
enum  servo_limrun_verdict_t {
  SERVO_LIMRUN_FOUND = 0 , SERVO_LIMRUN_FREE , SERVO_LIMRUN_PASS , SERVO_LIMRUN_FAIL ,
  SERVO_LIMRUN_ABORTED
}
enum  servo_limrun_why_t {
  SERVO_LIMRUN_WHY_NONE = 0 , SERVO_LIMRUN_WHY_TEST , SERVO_LIMRUN_WHY_HARD , SERVO_LIMRUN_WHY_CLIPPED ,
  SERVO_LIMRUN_WHY_STALL , SERVO_LIMRUN_WHY_SEARCH_LONG , SERVO_LIMRUN_WHY_SUPPLY_CHANGED , SERVO_LIMRUN_WHY_BENCH ,
  SERVO_LIMRUN_WHY_NOT_KST , SERVO_LIMRUN_WHY_PROG , SERVO_LIMRUN_WHY_WRITE , SERVO_LIMRUN_WHY_OTHER_VALUES ,
  SERVO_LIMRUN_WHY_MAPPING , SERVO_LIMRUN_WHY_SMALLEST , SERVO_LIMRUN_WHY_WRITE_CAP , SERVO_LIMRUN_WHY_STILL_BINDS ,
  SERVO_LIMRUN_WHY_NO_VOLTAGE , SERVO_LIMRUN_WHY_NO_STOP , SERVO_LIMRUN_WHY_BEFORE_NEUTRAL , SERVO_LIMRUN_WHY_NEUTRAL ,
  SERVO_LIMRUN_WHY_RANGE , SERVO_LIMRUN_WHY_COUNT
}
 Why a run failed or was aborted. More...

Functions

void servo_limrun_init (servo_limrun_t *r)
 Clear a run state to idle.
bool servo_limrun_start (servo_limrun_t *r, const servo_limrun_cfg_t *cfg, uint32_t now_ms)
 Start at now_ms. False, and nothing started, for a configuration whose centre is not between its MIN and MAX.
void servo_limrun_window (servo_limrun_t *r, const servo_test_win_t *w)
 One INA3221 window, as the automatic test takes it.
void servo_limrun_supply (servo_limrun_t *r, const servo_test_reading_t *s, uint32_t now_ms)
 The supply's reading.
void servo_limrun_connected (servo_limrun_t *r, const servo_limrun_servo_t *sv, uint32_t now_ms)
 The programmer's answers: the servo read after CONNECT, a write's end with the values the servo holds (servo_limrun_pulsed() for the pulse write: NEUTRAL, PULSE LOWER, PULSE UPPER), and the port back in PWM.
void servo_limrun_written (servo_limrun_t *r, bool ok, const uint16_t *angle, uint32_t now_ms)
 Feed the end of a write: the values the servo holds.
void servo_limrun_pulsed (servo_limrun_t *r, bool ok, const uint16_t *pulse_dus, uint32_t now_ms)
 Feed the end of the pulse write: the pulses the servo holds.
void servo_limrun_left (servo_limrun_t *r, bool ok, uint32_t now_ms)
 Feed the answer to LEAVE: the port is given back.
void servo_limrun_supply_refused (servo_limrun_t *r, bool output_on, uint32_t now_ms)
 The caller will not hand the programmer's CONNECT a PD mini that the run did not start with, or one whose output is off (output_on false). A supply the run started with, its output off, ends the run as a reading outside a session does (SERVO_TEST_AB_SUPPLY_OFF); one the run did not start with ends it as a changed supply (SERVO_LIMRUN_WHY_SUPPLY_CHANGED).
void servo_limrun_backup (servo_limrun_t *r, const char *name)
 The file the servo's values before the run were saved in, once the programmer names it: its first write saves it.
void servo_limrun_refused (servo_limrun_t *r, uint32_t now_ms)
 The programmer refused the CONNECT, write or LEAVE asked: it never started.
void servo_limrun_no_servo (servo_limrun_t *r, uint32_t now_ms)
 The programmer's CONNECT ended with no servo answering (KST_SES_ERR_NO_SERVO).
void servo_limrun_step (servo_limrun_t *r, uint32_t now_ms, const servo_limrun_in_t *in, servo_limrun_do_t *out)
 Advance to now_ms: what to do comes back in out.
void servo_limrun_abort (servo_limrun_t *r, servo_test_abort_t why, uint32_t now_ms)
 The caller's reasons.
bool servo_limrun_running (const servo_limrun_t *r)
 Tell whether the run is RUNNING.
bool servo_limrun_switching (const servo_limrun_t *r)
 The run asked the bench to SAFE or to ARMED and waits for it, or holds the programmer: a disarm then is its own, not the operator's. Not while the servo returns to the centre before a session: a disarm then is the operator's.
bool servo_limrun_programming (const servo_limrun_t *r)
 The run holds the programmer: CONNECT to LEAVE.
bool servo_limrun_wrote (const servo_limrun_t *r)
 A value was written and the servo may not hold its own any more.
bool servo_limrun_restore (const servo_limrun_t *r)
 The run ended as FAIL or ABORTED with the servo's values changed: RESTORE gives back the ones before it.
bool servo_limrun_original (const servo_limrun_t *r, uint16_t *left, uint16_t *right)
 The values the servo held before the run, valid once read.
servo_test_out_t servo_limrun_peek (servo_limrun_t *r, const char **text)
 The report's lines, in order, then END; OPEN_TXT first.
void servo_limrun_pop (servo_limrun_t *r)
 Take the outbox entry last returned by servo_limrun_peek().
bool servo_limrun_drained (const servo_limrun_t *r)
 Tell whether the run has nothing left to hand over.
bool servo_limrun_report_line (const servo_limrun_t *r, unsigned idx, char *buf, size_t n)
 Line idx of the report into buf; false past the last.
servo_str_t servo_limrun_verdict_str (servo_limrun_verdict_t v)
 The table entries the screen and the report name these by.
servo_str_t servo_limrun_why_str (const servo_limrun_t *r)
 Name table entry for the reason of a run.
servo_str_t servo_limrun_phase_str (servo_limrun_phase_t ph)
 Table entry for a phase of the limit run.
int32_t servo_limrun_dist (const servo_limrun_servo_t *sv, uint8_t side, uint16_t p_us)
 The share of travel, 0.1us of pulse: p_us's distance from NEUTRAL on side, positive outward, and the pulse limit's.
int32_t servo_limrun_span (const servo_limrun_servo_t *sv, uint8_t side)
 Span of one side, as servo_limrun_span() reports it.
uint16_t servo_limrun_overset (uint16_t v0, int32_t d, int32_t span, uint16_t lo_raw)
 The overset for value v0 and share d / span: ceil(v0 x share x (100 + SERVO_LIMRUN_OVERSET_PCT) / 100), within lo_raw to v0.
uint16_t servo_limrun_scaled (uint16_t v, int32_t d, int32_t span, uint16_t lo_raw)
 The final value for value v and share d / span: floor(v x share), within lo_raw to v.

Detailed Description

The limit run: the limit search (servo_limit.h) run on the SERVO screen against the INA3221's windows, and for a KST or Chaservo servo on the programming port, the servo's own travel limits lowered until neither end binds.

The search. A side is searched from its centre outward, a servo_limit search in steps of 10us, the position bound once its current is above 1.8 times the free current and 0.15A above it. A side whose end is measured free is free. The endpoints are reported; the bench's own pulse range is not changed. Where the search runs depends on the target:

  • The bench's own PWM output (cfg.read off): from PULSE CENTRE out to PULSE MIN and PULSE MAX in force. Nothing is read or written.
  • A KST or Chaservo servo on the programming port (cfg.read on): a read session comes first – the servo held at PULSE CENTRE, the bench switched to SAFE, CONNECT with its read, LEAVE, the bench switched to ARMED again – and the search runs from the servo's NEUTRAL out to its own PULSE LOWER and PULSE UPPER, never past them. A read that fails ends the run before any search. NEUTRAL not between PULSE LOWER and PULSE UPPER ends it as FAIL (SERVO_LIMRUN_WHY_NEUTRAL); PULSE LOWER to PULSE UPPER not inside the bench's PULSE MIN to PULSE MAX ends it as FAIL (SERVO_LIMRUN_WHY_RANGE), the bench left SAFE and nothing driven.

SERVO PULSES (cfg.set_pulses, programming port only). At 1000-2000 the read session writes PULSE LOWER 1000.0us and PULSE UPPER 2000.0us before anything is driven, and reads them back; NEUTRAL is not written. A servo at those values already gets no write. The search then runs out to 1000us and 2000us. The pulse write counts toward SERVO_LIMRUN_WRITES_MAX and RESTORE takes it back with the rest.

The programming (cfg.program). LEFT ANGLE and RIGHT ANGLE are the servo's own travel limits, one register each, in a unit that is not known. The run takes the servo as linear: PULSE LOWER and PULSE UPPER are the input pulses at which it reaches the full travel LEFT ANGLE and RIGHT ANGLE set, between NEUTRAL and such a pulse its travel is linear in the pulse, and past it the servo holds that travel. That is an assumption, not a measurement. The share of a side at pulse p is

f = (p - NEUTRAL) / (P_lim - NEUTRAL)

with P_lim PULSE LOWER on the short-pulse side and PULSE UPPER on the long one, both as the servo holds them when the search runs. f is formed from input pulses only, so REVERSION does not enter it: REVERSION decides which register limits that side, and the run finds that (below). A bound side whose last free pulse is not past NEUTRAL ends the run as FAIL (SERVO_LIMRUN_WHY_BEFORE_NEUTRAL) before any write.

Two writes a bound side, v0 the value its register holds at the read:

  1. The overset: v1 = ceil(v0 x fb x (100 + SERVO_LIMRUN_OVERSET_PCT) / 100), fb the share at the first search's first bound pulse, at most v0 and at least the smallest value the hard rules take. The first bound pulse lies at or past the stop, so the stop stays reachable, now close to the pulse limit, and the side is searched again. Free there, the run ends as FAIL (SERVO_LIMRUN_WHY_NO_STOP) with no second write: the stop the overset was to keep in reach is not found. An overset search that binds far inside the pulse limit still gives the final value below; no further overset follows.
  2. The final value: v2 = floor(v1 x f1), f1 the share at the overset search's last free pulse, at least the smallest value. No margin is added: the last free pulse lies a 10us step short of the bound one.

Worked: NEUTRAL 1500.2us, PULSE LOWER 900.0us, v0 150, the stop at 1000us. The first search is free at 1000us and bound at 990us: fb = 510.2 / 600.2 = 0.850, v1 = ceil(150 x 0.850 x 1.03) = 132. A linear servo at 132 is free out to 930us: f1 = 570.2 / 600.2 = 0.950, v2 = floor(132 x 0.950) = 125. At 125 the full travel ends at 1000.2us.

The overset is left out, and the first search counts as its search, where v1 would not lie below v0 (fb above about 0.97) or where the model moves the stop, estimated at the first search's last free pulse, less than SERVO_LIMRUN_MOVED_US further out: d0 x (v0 - v1) / v1 under 20us, d0 that pulse's distance from NEUTRAL. A search could not tell that move from no move. The final value is then v2 = floor(v0 x f0), f0 the share at the first search's last free pulse.

Which pulse side each register limits is not stated by the protocol notes, so the run finds it on the first bound side's first write: that value goes to LEFT ANGLE (RIGHT ANGLE where LEFT ANGLE holds the smallest value already) and the side is searched. The end moved when it is free now or its endpoint lies SERVO_LIMRUN_MOVED_US further out; it stayed when its endpoint lies within that of where it was. An end that stayed sends LEFT ANGLE back to its value and the value, worked out again from RIGHT ANGLE's v0, to RIGHT ANGLE; stayed again, the run ends as FAIL. An end that moved inward ends it as FAIL too: nothing about the servo is guessed. The register found limits that side, the other one the other side; a register whose side never bound keeps its value.

Last, both ends are searched again: PASS when both are free, FAIL when either binds. No further write follows a FAIL; RESTORE gives the values before the run back.

Writes. A value is written in a session of the programmer: the servo held at the centre for SERVO_LIMRUN_RETURN_MS after the search, so a session never starts with the servo against an end, the bench switched to SAFE, the port taken (CONNECT reads all registers), the write, the port given back (LEAVE), the bench switched to ARMED again. The read session writes nothing but the pulses of SERVO PULSES; each angle write has a session of its own. A LEAVE this link or supply does not complete ends the run before any value changes. No step of the protocol is known to make a value permanent, and no RAM-only write exists: every write is the register write the programmer reads back. The run counts register values written and stops at SERVO_LIMRUN_WRITES_MAX, the most this flow can need.

Protections, for the whole run, whatever phase it is in:

  • a 1ms sample of the window at or above SERVO_LIMRUN_HARD_MA, or a clipped one (the INA3221 at the end of its range), ends it;
  • window means above SERVO_LIMRUN_STALL_MA for SERVO_LIMRUN_STALL_MS end it;
  • no window for SERVO_LIMRUN_WIN_STALE_MS ends it;
  • each search has its own cap, SERVO_LIMRUN_SEARCH_MS;
  • the search's own protections stay (servo_limit.h).

The supply is also held to what it was at the start, its set points and its output, in every phase but the programmer's sessions (CONNECT to LEAVE): those own the port and the supply (kst_prog.h), and a change then is theirs. While a search drives the servo the rail voltage stays within SERVO_LIMRUN_RAIL_DV_MV of the run's first window. A search's window without the rail voltage ends the run (SERVO_LIMRUN_WHY_NO_VOLTAGE), so that reference is a window's and no window escapes the band. A window without a current reading is not counted at all: the meter's silence runs on through it.

The caller ends the run (servo_limrun_abort()) for STOP, SAFE, link loss, leaving the screen, the operator taking the servo, changed settings, lost touch events, the INA3221 no longer the meter, and a supply that stops answering, trips, switches off or has its set points changed. An ended run asks the output off and lets the servo go (servo_limrun_do_t.off and release), and asks SAFE where it waited for ARMED; the values last written stay in the servo.

Pure: fed windows, the programmer's results and the clock, it moves nothing itself. Its report leaves through an outbox (servo_limrun_peek()) as one LIMITnnn.TXT.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ SERVO_LIMRUN_RAIL_MIN_MV

#define SERVO_LIMRUN_RAIL_MIN_MV   3000

The servo rail a run starts on at least, the last INA3221 window's mean (the screen's START check): an output that is off reads 0V, and a search there finds two free ends on a servo that never moved.

3.0V lies under the 4.8V servos are commonly specified from, so a single lithium cell's rail, 3.0V to 4.2V, still starts. The voltage a given servo stops moving at is not measured.

Enumeration Type Documentation

◆ servo_limrun_phase_t

Enumerator
SERVO_LIMRUN_PH_SEARCH 

the first search of a side

SERVO_LIMRUN_PH_SAFE 

the servo back at the centre, then the bench on its way to SAFE

SERVO_LIMRUN_PH_ARM 

the bench on its way to ARMED

SERVO_LIMRUN_PH_PROBE 

a side searched after a write: the overset's search, or the first write's for the side it limits

SERVO_LIMRUN_PH_CHECK 

both sides searched at the end

◆ servo_limrun_verdict_t

Enumerator
SERVO_LIMRUN_FOUND 

search only: the ends as found

SERVO_LIMRUN_FREE 

neither end binds: nothing written

SERVO_LIMRUN_PASS 

programmed, both ends free after

◆ servo_limrun_why_t

Why a run failed or was aborted.

Enumerator
SERVO_LIMRUN_WHY_TEST 

a reason of servo_test_abort_t, in test_why

SERVO_LIMRUN_WHY_HARD 

a sample at SERVO_LIMRUN_HARD_MA

SERVO_LIMRUN_WHY_CLIPPED 

a sample at the meter's end

SERVO_LIMRUN_WHY_STALL 

above SERVO_LIMRUN_STALL_MA

SERVO_LIMRUN_WHY_SEARCH_LONG 

a search past its cap

SERVO_LIMRUN_WHY_SUPPLY_CHANGED 

set points or rail voltage moved

SERVO_LIMRUN_WHY_BENCH 

the bench did not switch

SERVO_LIMRUN_WHY_NOT_KST 

no KST or Chaservo servo read

SERVO_LIMRUN_WHY_PROG 

CONNECT or LEAVE did not end well

SERVO_LIMRUN_WHY_WRITE 

a write refused or lost

SERVO_LIMRUN_WHY_OTHER_VALUES 

the servo read holds other values

SERVO_LIMRUN_WHY_MAPPING 

which end LEFT ANGLE moves is not found

SERVO_LIMRUN_WHY_SMALLEST 

binds at the smallest value

SERVO_LIMRUN_WHY_WRITE_CAP 

SERVO_LIMRUN_WRITES_MAX reached

SERVO_LIMRUN_WHY_STILL_BINDS 

an end binds after programming

SERVO_LIMRUN_WHY_NO_VOLTAGE 

a search's window without the rail voltage

SERVO_LIMRUN_WHY_NO_STOP 

free after the overset write

SERVO_LIMRUN_WHY_BEFORE_NEUTRAL 

a free end not past NEUTRAL

SERVO_LIMRUN_WHY_NEUTRAL 

NEUTRAL not between PULSE LOWER and PULSE UPPER

SERVO_LIMRUN_WHY_RANGE 

PULSE LOWER to PULSE UPPER not inside PULSE MIN to PULSE MAX

Function Documentation

◆ servo_limrun_abort()

void servo_limrun_abort ( servo_limrun_t * r,
servo_test_abort_t why,
uint32_t now_ms )

The caller's reasons.

Parameters
rRun state. Does nothing unless the run is RUNNING.
whyReason from the caller.
now_msTime of the abort, ms.

◆ servo_limrun_backup()

void servo_limrun_backup ( servo_limrun_t * r,
const char * name )

The file the servo's values before the run were saved in, once the programmer names it: its first write saves it.

Parameters
rRun state.
nameFile name. Ignored when NULL or empty, or when the servo was not read.

◆ servo_limrun_connected()

void servo_limrun_connected ( servo_limrun_t * r,
const servo_limrun_servo_t * sv,
uint32_t now_ms )

The programmer's answers: the servo read after CONNECT, a write's end with the values the servo holds (servo_limrun_pulsed() for the pulse write: NEUTRAL, PULSE LOWER, PULSE UPPER), and the port back in PWM.

Parameters
rRun state.
svServo as read. Ignored when NULL.
now_msTime of the answer, ms.

◆ servo_limrun_dist()

int32_t servo_limrun_dist ( const servo_limrun_servo_t * sv,
uint8_t side,
uint16_t p_us )

The share of travel, 0.1us of pulse: p_us's distance from NEUTRAL on side, positive outward, and the pulse limit's.

Parameters
svServo pulses, 0.1us.
sideSERVO_LIMRUN_LO or SERVO_LIMRUN_HI.
p_usPulse width, microseconds.
Returns
distance of p_us from NEUTRAL on side, 0.1us, positive outward.

◆ servo_limrun_drained()

bool servo_limrun_drained ( const servo_limrun_t * r)

Tell whether the run has nothing left to hand over.

Parameters
rRun state.
Returns
true when r is NULL or IDLE, or DONE with the report handed out.

◆ servo_limrun_init()

void servo_limrun_init ( servo_limrun_t * r)

Clear a run state to idle.

Parameters
rRun state. Does nothing when NULL.

◆ servo_limrun_left()

void servo_limrun_left ( servo_limrun_t * r,
bool ok,
uint32_t now_ms )

Feed the answer to LEAVE: the port is given back.

Parameters
rRun state.
okfalse ends the run as ABORTED with PROG.
now_msTime of the answer, ms.

◆ servo_limrun_no_servo()

void servo_limrun_no_servo ( servo_limrun_t * r,
uint32_t now_ms )

The programmer's CONNECT ended with no servo answering (KST_SES_ERR_NO_SERVO).

The run's first CONNECT ends it as FAIL with SERVO_LIMRUN_WHY_NOT_KST, as a read that is no KST or Chaservo servo does; a later CONNECT ends it as servo_limrun_refused() does, with SERVO_LIMRUN_WHY_PROG. Nothing at any other step.

Parameters
rThe run; NULL, or one not running, is left as it is.
now_msCurrent time, ms.

◆ servo_limrun_original()

bool servo_limrun_original ( const servo_limrun_t * r,
uint16_t * left,
uint16_t * right )

The values the servo held before the run, valid once read.

Parameters
rRun state.
leftOut: LEFT ANGLE before the run. NULL to skip.
rightOut: RIGHT ANGLE before the run. NULL to skip.
Returns
true when the servo was read; false otherwise.

◆ servo_limrun_overset()

uint16_t servo_limrun_overset ( uint16_t v0,
int32_t d,
int32_t span,
uint16_t lo_raw )

The overset for value v0 and share d / span: ceil(v0 x share x (100 + SERVO_LIMRUN_OVERSET_PCT) / 100), within lo_raw to v0.

Parameters
v0Register value at the read.
dDistance of the bound pulse from NEUTRAL, 0.1us. 0 or less returns the lower bound.
spanSpan of the side, 0.1us. 0 or less returns the lower bound.
lo_rawSmallest value the hard rules take.
Returns
overset value, within lo_raw to v0; v0 when lo_raw is above v0.

◆ servo_limrun_peek()

servo_test_out_t servo_limrun_peek ( servo_limrun_t * r,
const char ** text )

The report's lines, in order, then END; OPEN_TXT first.

Parameters
rRun state. NULL gives NONE.
textOut: text of the entry; empty when there is none.
Returns
kind of the next outbox entry: OPEN_TXT first, then TXT lines, then END; NONE when the run is not DONE or the report was handed out.

◆ servo_limrun_phase_str()

servo_str_t servo_limrun_phase_str ( servo_limrun_phase_t ph)

Table entry for a phase of the limit run.

Parameters
phPhase to name.
Returns
table entry for the phase; PHASE_NONE for a value not in the enum.

◆ servo_limrun_pop()

void servo_limrun_pop ( servo_limrun_t * r)

Take the outbox entry last returned by servo_limrun_peek().

Parameters
rRun state.

◆ servo_limrun_programming()

bool servo_limrun_programming ( const servo_limrun_t * r)

The run holds the programmer: CONNECT to LEAVE.

Parameters
rRun state. NULL gives false.
Returns
true while running in CONNECT, WRITE or LEAVE.

◆ servo_limrun_pulsed()

void servo_limrun_pulsed ( servo_limrun_t * r,
bool ok,
const uint16_t * pulse_dus,
uint32_t now_ms )

Feed the end of the pulse write: the pulses the servo holds.

Parameters
rRun state.
okfalse ends the run as ABORTED with WRITE.
pulse_dusNEUTRAL, PULSE LOWER and PULSE UPPER, 0.1us. NULL ends the run as WRITE.
now_msTime of the answer, ms.

◆ servo_limrun_refused()

void servo_limrun_refused ( servo_limrun_t * r,
uint32_t now_ms )

The programmer refused the CONNECT, write or LEAVE asked: it never started.

Parameters
rRun state.
now_msTime of the refusal, ms.

◆ servo_limrun_report_line()

bool servo_limrun_report_line ( const servo_limrun_t * r,
unsigned idx,
char * buf,
size_t n )

Line idx of the report into buf; false past the last.

Parameters
rRun state.
idxLine index, from 0.
bufOut: line text.
nSize of buf, bytes.
Returns
true when line idx exists; false for NULL arguments, n of 0, or a run not DONE.

◆ servo_limrun_restore()

bool servo_limrun_restore ( const servo_limrun_t * r)

The run ended as FAIL or ABORTED with the servo's values changed: RESTORE gives back the ones before it.

Parameters
rRun state.
Returns
true when the run is DONE, programmed, and ended as FAIL or ABORTED; false for NULL.

◆ servo_limrun_running()

bool servo_limrun_running ( const servo_limrun_t * r)

Tell whether the run is RUNNING.

Parameters
rRun state. NULL gives false.
Returns
true when the state is RUNNING.

◆ servo_limrun_scaled()

uint16_t servo_limrun_scaled ( uint16_t v,
int32_t d,
int32_t span,
uint16_t lo_raw )

The final value for value v and share d / span: floor(v x share), within lo_raw to v.

Parameters
vValue the share applies to.
dDistance of the free pulse from NEUTRAL, 0.1us. 0 or less returns the lower bound.
spanSpan of the side, 0.1us. 0 or less returns the lower bound.
lo_rawSmallest value the hard rules take.
Returns
final value, within lo_raw to v; v when lo_raw is above v.

◆ servo_limrun_span()

int32_t servo_limrun_span ( const servo_limrun_servo_t * sv,
uint8_t side )

Span of one side, as servo_limrun_span() reports it.

Parameters
svServo pulses, 0.1us.
sideSERVO_LIMRUN_LO or SERVO_LIMRUN_HI.
Returns
distance from NEUTRAL to the side pulse limit, 0.1us.

◆ servo_limrun_start()

bool servo_limrun_start ( servo_limrun_t * r,
const servo_limrun_cfg_t * cfg,
uint32_t now_ms )

Start at now_ms. False, and nothing started, for a configuration whose centre is not between its MIN and MAX.

Parameters
rRun state. Cleared before the run starts.
cfgConfiguration of the run.
now_msStart time, ms.
Returns
true when the run started; false for a NULL r or cfg, a run already RUNNING, a centre not strictly between min_us and max_us, or program or set_pulses without read.

◆ servo_limrun_step()

void servo_limrun_step ( servo_limrun_t * r,
uint32_t now_ms,
const servo_limrun_in_t * in,
servo_limrun_do_t * out )

Advance to now_ms: what to do comes back in out.

Parameters
rRun state.
now_msCurrent time, ms.
inWhat the caller knows this step. NULL skips the checks on it.
outOut: cleared, then filled with what the run wants done. NULL to skip.

◆ servo_limrun_supply()

void servo_limrun_supply ( servo_limrun_t * r,
const servo_test_reading_t * s,
uint32_t now_ms )

The supply's reading.

The first one after the start, answering or not, is what it must stay at: its set points, and its output on or off. An output off then and on later, or a supply that did not answer and answers later, its output on or off, ends the run as a changed supply (SERVO_LIMRUN_WHY_SUPPLY_CHANGED). The caller passes a reading that does not answer where it has none at the start.

Parameters
rRun state.
sSupply reading. Ignored when NULL or during the programmer session.
now_msTime of the reading, ms.

◆ servo_limrun_supply_refused()

void servo_limrun_supply_refused ( servo_limrun_t * r,
bool output_on,
uint32_t now_ms )

The caller will not hand the programmer's CONNECT a PD mini that the run did not start with, or one whose output is off (output_on false). A supply the run started with, its output off, ends the run as a reading outside a session does (SERVO_TEST_AB_SUPPLY_OFF); one the run did not start with ends it as a changed supply (SERVO_LIMRUN_WHY_SUPPLY_CHANGED).

Parameters
rRun state.
output_onWhether the supply output is on.
now_msTime of the refusal, ms.

◆ servo_limrun_switching()

bool servo_limrun_switching ( const servo_limrun_t * r)

The run asked the bench to SAFE or to ARMED and waits for it, or holds the programmer: a disarm then is its own, not the operator's. Not while the servo returns to the centre before a session: a disarm then is the operator's.

Parameters
rRun state. NULL gives false.
Returns
true while running in CONNECT, WRITE, LEAVE or ARM, or in SAFE after SAFE was asked.

◆ servo_limrun_verdict_str()

servo_str_t servo_limrun_verdict_str ( servo_limrun_verdict_t v)

The table entries the screen and the report name these by.

Parameters
vVerdict to name.
Returns
table entry for FOUND, FREE, PASS or FAIL; ABORTED for any other value.

◆ servo_limrun_why_str()

servo_str_t servo_limrun_why_str ( const servo_limrun_t * r)

Name table entry for the reason of a run.

Parameters
rRun state. NULL gives the NONE entry.
Returns
table entry for the reason; the test reason for WHY_TEST; NONE for other values.

◆ servo_limrun_window()

void servo_limrun_window ( servo_limrun_t * r,
const servo_test_win_t * w )

One INA3221 window, as the automatic test takes it.

Parameters
rRun state.
wWindow to feed. Ignored when NULL or when it has no current reading.

◆ servo_limrun_written()

void servo_limrun_written ( servo_limrun_t * r,
bool ok,
const uint16_t * angle,
uint32_t now_ms )

Feed the end of a write: the values the servo holds.

Parameters
rRun state.
okfalse ends the run as ABORTED with WRITE.
angleLEFT and RIGHT ANGLE the servo holds, two values. NULL ends the run as WRITE.
now_msTime of the answer, ms.

◆ servo_limrun_wrote()

bool servo_limrun_wrote ( const servo_limrun_t * r)

A value was written and the servo may not hold its own any more.

Parameters
rRun state.
Returns
true when at least one register value was written; false for NULL.