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

The SERVO link page. More...

#include "servo_page.h"
#include <stddef.h>
#include <string.h>
#include "link_msg.h"
#include "out_pwm_map.h"
#include "outputs_pages.h"
#include "prog_port.h"

Functions

void servo_page_init (servo_page_t *p)
 Set the page to its start: no sweep, no hold, the standard servo ports as target.
static sweep_cfg_t cfg_of (const uint16_t *r)
 The sweep curve the page's registers describe.
static bool same_curve (const sweep_cfg_t *a, const sweep_cfg_t *b)
 Whether two sweeps trace the same curve; the movement count is not the curve, and it applies to the next start.
static uint8_t target_first (const servo_page_t *p)
 The channels the sweep and the hold command: the standard servo ports, or the range servo_page_target() named.
static uint8_t target_end (const servo_page_t *p)
 One past the last channel the sweep and the hold command.
static void command_surfaces (const servo_page_t *p, outputs_t *o, uint16_t cmd, uint32_t now_ms)
 Every surface of the target commanded to cmd.
static void freeze_surfaces (const servo_page_t *p, outputs_t *o)
 Every surface held where its output has got to.
static bool resumable (const servo_page_t *p, const uint16_t *next)
 Whether a RESUME can carry on the sweep held now: a phase kept by a hold still in force and a page that still describes that sweep.
static uint8_t rate_check (const servo_page_t *p, const outputs_t *o, uint16_t hz)
 Whether the frame rate hz may be the target's.
uint8_t servo_page_write (servo_page_t *p, uint8_t off, uint8_t n, const uint16_t *in, outputs_t *o, uint32_t now_ms)
 A write to the page, validated whole against the bank o before any of it is stored.
void servo_page_read (servo_page_t *p, uint8_t off, uint8_t n, uint16_t *out)
 The page as a read sees it: the sweep register 0 once the sweep has stopped, and the ends it reached as of the last servo_page_step().
bool servo_page_step (servo_page_t *p, outputs_t *o, uint32_t now_ms)
 One pass: stop a sweep the bank's surfaces are no longer armed for, one not written for OUT_DEFAULT_TIMEOUT_MS or one that has made its movements, and command every surface along a sweep that runs.
bool servo_page_target (servo_page_t *p, outputs_t *o, uint8_t first, uint8_t count)
 Name the channels the sweep and the hold command: count channels of the bank from first, or with count 0 the standard servo ports (channels 0 to LINK_OUT_CHANNELS - 1), which is what a page starts with.
void servo_page_ports (servo_page_t *p, uint16_t channels, uint16_t sliced)
 The programming ports as a rate for a port target is judged, bit n for bank channel n: channels the ports whose pulse is on, whose endpoints the rate must fit, and sliced the ports whose pin shares a PWM slice with another port's pin, which run at PROG_PORT_RATE_HZ only.
uint16_t servo_page_hz (const servo_page_t *p)
 The frame rate register: the target's rate, 0 for its own.
uint16_t servo_page_slots_hz (const servo_page_t *p)
 The rate the slots take, for outputs_slot_rates() and the OUTPUTS and CHAN_CFG checks: the register with the standard servo ports as target, 0 with a programming port.
servo_resume_t servo_page_resume_plan (bool asked, bool held, bool timed, uint16_t proto_minor, bool refused)
 The host's choice for a sweep command: asked a resume of the sweep held, held a hold at the far end, timed its phase known here (false for a HOLD that went unanswered and was not yet acknowledged, which may or may not have reached the far end), proto_minor its protocol minor, and refused the RESUME just written was refused.
void servo_phase_started (servo_phase_t *ph, uint32_t ack_ms)
 A sweep started, from its beginning, when acknowledged at ack_ms.
void servo_phase_stopped (servo_phase_t *ph)
 The sweep stopped, or the far end restarted: nothing is held to resume.
bool servo_phase_resumable (const servo_phase_t *ph)
 Whether a hold of a running sweep stands to be resumed.
uint32_t servo_phase_held (servo_phase_t *ph, uint32_t ack_ms)
 A running sweep held at ack_ms: its phase is kept; returned.
bool servo_phase_resumed (servo_phase_t *ph, uint32_t ack_ms, uint32_t *start_ms)
 A held sweep resumed at ack_ms: its phase 0 moves on by the hold, into start_ms.

Detailed Description

The SERVO link page.

See servo_page.h.

SPDX-License-Identifier: MIT

Function Documentation

◆ cfg_of()

sweep_cfg_t cfg_of ( const uint16_t * r)
static

The sweep curve the page's registers describe.

Parameters
rThe page registers, LINK_SV_COUNT of them.
Returns
The curve: kind, MHz, amplitude, dwell and movements.

◆ command_surfaces()

void command_surfaces ( const servo_page_t * p,
outputs_t * o,
uint16_t cmd,
uint32_t now_ms )
static

Every surface of the target commanded to cmd.

Parameters
pThe page state, for the target range.
oThe outputs bank. Only channels with role OUT_ROLE_SURFACE change.
cmdThe command for each surface.
now_msCurrent time in milliseconds, passed to outputs_set().

◆ freeze_surfaces()

void freeze_surfaces ( const servo_page_t * p,
outputs_t * o )
static

Every surface held where its output has got to.

The last command a sweep left would otherwise be slewed towards for as long again as the channel's own timeout, past the moment the sweep stopped. The channel's clock is left alone, so it rests when it would have.

Parameters
pThe page state, for the target range.
oThe outputs bank. Each surface's command becomes its actual value.

◆ rate_check()

uint8_t rate_check ( const servo_page_t * p,
const outputs_t * o,
uint16_t hz )
static

Whether the frame rate hz may be the target's.

The standard servo ports: every PWM slot whose first channel is a surface, judged against the slices it shares (outputs_servo_rate_check()) and against every PWM slot's endpoints at the rate it runs at, 0 sending the surfaces back to their own. A programming port: its own pin alone, which shares no slice with a slot; a pin that shares its slice with another port's stays at PROG_PORT_RATE_HZ, and a port whose pulse is on takes no rate its endpoints do not fit. 0 always fits: the port goes back to 50Hz, whose 19999us frame holds every endpoint the pages take.

Parameters
pThe page state, for the target and the port masks.
oThe outputs bank, for the slots and the channels' endpoints.
hzThe frame rate in Hz to judge.
Returns
0 when hz fits. Otherwise the refusal of outputs_servo_rate_check() or outputs_pulse_check() for the standard servo ports, or LINK_NACK_BAD_VALUE for a programming port whose range or endpoints do not fit hz.

◆ resumable()

bool resumable ( const servo_page_t * p,
const uint16_t * next )
static

Whether a RESUME can carry on the sweep held now: a phase kept by a hold still in force and a page that still describes that sweep.

A curve changed while held is a new sweep, which starts only whole.

Parameters
pThe page state.
nextThe page registers as the write would leave them.
Returns
true when a hold is in force, a paused sweep is kept, and the registers MHZ, SPAN, DWELL_MS and MOVES match that sweep.

◆ same_curve()

bool same_curve ( const sweep_cfg_t * a,
const sweep_cfg_t * b )
static

Whether two sweeps trace the same curve; the movement count is not the curve, and it applies to the next start.

Parameters
aThe first sweep.
bThe second sweep.
Returns
true when kind, mhz, amplitude and dwell_ms are equal.

◆ servo_page_hz()

uint16_t servo_page_hz ( const servo_page_t * p)

The frame rate register: the target's rate, 0 for its own.

Parameters
pThe page state.
Returns
The frame rate in Hz from register 0, or 0 for NULL.

◆ servo_page_init()

void servo_page_init ( servo_page_t * p)

Set the page to its start: no sweep, no hold, the standard servo ports as target.

Nothing is done when p is NULL.

Parameters
pThe page state, overwritten.

◆ servo_page_ports()

void servo_page_ports ( servo_page_t * p,
uint16_t channels,
uint16_t sliced )

The programming ports as a rate for a port target is judged, bit n for bank channel n: channels the ports whose pulse is on, whose endpoints the rate must fit, and sliced the ports whose pin shares a PWM slice with another port's pin, which run at PROG_PORT_RATE_HZ only.

Both 0 after servo_page_init().

Parameters
pThe page state. Nothing is done when NULL.
channelsBit n set for a bank channel whose pulse is on.
slicedBit n set for a bank channel that runs at PROG_PORT_RATE_HZ.

◆ servo_page_read()

void servo_page_read ( servo_page_t * p,
uint8_t off,
uint8_t n,
uint16_t * out )

The page as a read sees it: the sweep register 0 once the sweep has stopped, and the ends it reached as of the last servo_page_step().

Nothing is done when p or out is NULL, or when off and n reach past LINK_SV_COUNT.

Parameters
pThe page state. The sweep register is cleared when the sweep has stopped.
offFirst register, 0 to LINK_SV_COUNT - 1.
nNumber of registers to read.
outReceives the n register values.

◆ servo_page_resume_plan()

servo_resume_t servo_page_resume_plan ( bool asked,
bool held,
bool timed,
uint16_t proto_minor,
bool refused )

The host's choice for a sweep command: asked a resume of the sweep held, held a hold at the far end, timed its phase known here (false for a HOLD that went unanswered and was not yet acknowledged, which may or may not have reached the far end), proto_minor its protocol minor, and refused the RESUME just written was refused.

A resume is written only for a hold in force, so a repeat of the resumed sweep is the curve; the two fallbacks start the curve over – a stop, then the curve whole, since a RESUME whose acknowledgement was lost leaves the far end running and a repeat of its curve would carry it on – and are for saying so.

Parameters
askedWhether a resume of the held sweep was asked.
heldWhether a hold is in force at the far end.
timedWhether the phase of the hold is known on the host.
proto_minorThe IOmcu's protocol minor. Below 6 has no resume.
refusedWhether the RESUME just written was refused.
Returns
SERVO_RESUME_CURVE when nothing is asked or held, SERVO_RESUME_TOO_OLD below minor 6, SERVO_RESUME_UNTIMED when the phase is unknown, SERVO_RESUME_REFUSED when refused, and SERVO_RESUME_WRITE otherwise.

◆ servo_page_slots_hz()

uint16_t servo_page_slots_hz ( const servo_page_t * p)

The rate the slots take, for outputs_slot_rates() and the OUTPUTS and CHAN_CFG checks: the register with the standard servo ports as target, 0 with a programming port.

Parameters
pThe page state.
Returns
The frame rate in Hz when the target is the standard servo ports, otherwise 0. 0 for NULL.

◆ servo_page_step()

bool servo_page_step ( servo_page_t * p,
outputs_t * o,
uint32_t now_ms )

One pass: stop a sweep the bank's surfaces are no longer armed for, one not written for OUT_DEFAULT_TIMEOUT_MS or one that has made its movements, and command every surface along a sweep that runs.

True while it runs.

Parameters
pThe page state. Nothing is done when NULL.
oThe outputs bank whose surfaces are commanded.
now_msCurrent time in milliseconds.
Returns
true while the sweep runs, false when it has stopped or p or o is NULL.

◆ servo_page_target()

bool servo_page_target ( servo_page_t * p,
outputs_t * o,
uint8_t first,
uint8_t count )

Name the channels the sweep and the hold command: count channels of the bank from first, or with count 0 the standard servo ports (channels 0 to LINK_OUT_CHANNELS - 1), which is what a page starts with.

A programming port is such a range of 1 channel.

A change ends a running or held sweep as a SWEEP of 0 does: the surfaces it moved stay where their outputs are. It also returns the frame rate register to 0, so a change of target never raises a rate under pulses meant for a slower one (protocol 4.19). False, with nothing changed, for a range that runs off the bank.

Parameters
pThe page state.
oThe outputs bank, for the surfaces a running sweep stops on.
firstFirst channel of the target.
countNumber of channels; 0 selects the standard servo ports.
Returns
true when the target is in force, including when it is unchanged. false for NULL p or o, or a range past OUT_MAX_CHANNELS.

◆ servo_page_write()

uint8_t servo_page_write ( servo_page_t * p,
uint8_t off,
uint8_t n,
const uint16_t * in,
outputs_t * o,
uint32_t now_ms )

A write to the page, validated whole against the bank o before any of it is stored.

A stop holds each surface where its output has got to. Refused: off the page (BAD_RANGE); register 6 (READ_ONLY); a frame rate the target cannot take (BAD_VALUE): for the standard servo ports one outputs_servo_rate_check() refuses or one under which a bound PWM output would carry a pulse longer than its frame (outputs_pulse_check(), protocol 4.16); for a programming port one outside the PWM driver's 40 to 560Hz, one other than PROG_PORT_RATE_HZ for a port servo_page_ports() names as sharing its slice, or one whose frame is shorter than the endpoint of a port it names as pulsing (protocol 4.19). Refused too: a sweep sweep_cfg_valid() refuses, or a sweep started or changed by a write that does not carry all four of its registers, or a resume with no phase kept or with a curve that has changed since the hold (BAD_VALUE); a sweep, a hold or a resume while the bank is not armed for the surfaces, an arm for the throttle alone among them (NOT_ARMED).

Parameters
pThe page state, changed only when the write is taken.
offFirst register, 0 to LINK_SV_COUNT - 1.
nNumber of registers to write. 0 is taken with no change.
inThe n new register values.
oThe outputs bank, judged and driven by the sweep and the hold.
now_msCurrent time in milliseconds, on the same clock as the passes.
Returns
0 when taken. LINK_NACK_BAD_RANGE for a NULL pointer or a range past LINK_SV_COUNT. LINK_NACK_READ_ONLY for a range that reaches register 6. LINK_NACK_BAD_VALUE and LINK_NACK_NOT_ARMED as described above.

◆ servo_phase_held()

uint32_t servo_phase_held ( servo_phase_t * ph,
uint32_t ack_ms )

A running sweep held at ack_ms: its phase is kept; returned.

Parameters
phThe phase record. Nothing is changed when NULL.
ack_msHost time of the hold's acknowledgement, in milliseconds.
Returns
The kept phase in milliseconds: ack_ms minus the start. 0 for NULL.

◆ servo_phase_resumable()

bool servo_phase_resumable ( const servo_phase_t * ph)

Whether a hold of a running sweep stands to be resumed.

Parameters
phThe phase record.
Returns
true when a phase is kept, false otherwise or for NULL.

◆ servo_phase_resumed()

bool servo_phase_resumed ( servo_phase_t * ph,
uint32_t ack_ms,
uint32_t * start_ms )

A held sweep resumed at ack_ms: its phase 0 moves on by the hold, into start_ms.

False, and nothing changes, with no phase kept.

Parameters
phThe phase record. Its kept phase is cleared on success.
ack_msHost time of the resume's acknowledgement, in milliseconds.
start_msReceives the new phase 0 on the host's clock. May be NULL.
Returns
true when a phase was kept and resumed, false otherwise or for NULL.

◆ servo_phase_started()

void servo_phase_started ( servo_phase_t * ph,
uint32_t ack_ms )

A sweep started, from its beginning, when acknowledged at ack_ms.

Nothing is done when ph is NULL.

Parameters
phThe phase record, set to the start and no kept phase.
ack_msHost time of the acknowledgement, in milliseconds.

◆ servo_phase_stopped()

void servo_phase_stopped ( servo_phase_t * ph)

The sweep stopped, or the far end restarted: nothing is held to resume.

Nothing is done when ph is NULL.

Parameters
phThe phase record. Its kept phase is cleared.

◆ target_end()

uint8_t target_end ( const servo_page_t * p)
static

One past the last channel the sweep and the hold command.

Parameters
pThe page state.
Returns
target_first + target_n, or LINK_OUT_CHANNELS for the standard servo ports.

◆ target_first()

uint8_t target_first ( const servo_page_t * p)
static

The channels the sweep and the hold command: the standard servo ports, or the range servo_page_target() named.

Parameters
pThe page state.
Returns
The first channel index, 0 for the standard servo ports.