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

The link's output pages, expressed as bank operations. More...

#include "outputs_pages.h"
#include <stddef.h>
#include <string.h>
#include "link_control.h"
#include "link_msg.h"
#include "link_pages.h"
#include "out_pwm_map.h"

Functions

static out_driver_t driver_of (uint16_t wire)
 The wire's driver numbers are a contract, so they are mapped rather than cast: a reordered bank enum leaves this correct.
uint16_t link_driver_of (out_driver_t d)
 A bank driver as the wire numbers it.
static bool known_driver (uint16_t wire)
 Whether a wire driver number is one this build knows.
void outputs_chan_cfg_defaults (uint16_t *regs)
 Sets the CHAN_CFG page to its defaults: every channel a surface, slew 0, and the default endpoints.
uint8_t outputs_chan_cfg_write (uint16_t *regs, uint8_t off, uint8_t n, const uint16_t *in)
 Stores a window of the CHAN_CFG page, refusing the whole window on a bad value.
void outputs_chan_cfg_restore (uint16_t *regs, const uint16_t *saved)
 The CHAN_CFG page saved, kept in flash, into regs at start.
uint8_t outputs_chan_cfg_armed_check (const uint16_t *regs, const uint16_t *next, bool armed)
 Whether a CHAN_CFG or OUTPUTS write is refused because the bench is armed: 0, or LINK_NACK_BAD_VALUE, the reason the SENSE page gives for the same rule.
uint8_t outputs_slots_armed_check (const uint16_t *regs, const uint16_t *next, bool armed)
 The OUTPUTS write counterpart of outputs_chan_cfg_armed_check().
bool outputs_chan_cfg_set_throttle_range (uint16_t *regs, uint16_t min_us, uint16_t max_us)
 Give every channel regs marks as a throttle the endpoints min_us and max_us, and leave every other channel as it is.
void outputs_chan_cfg_apply (outputs_t *o, const uint16_t *regs)
 Puts a CHAN_CFG page into the bank: role, slew and endpoints of each channel.
static bool servo_rate_reaches (const outputs_t *o, unsigned i)
 Whether the SERVO page's rate, when not 0, is the one slot i runs at.
void outputs_slot_rates (const outputs_t *o, uint16_t servo_hz, uint16_t *rate)
 The frame rate each slot of o is bound at, into rate (OUT_MAX_SLOTS entries): the slot's own, or servo_hz for a PWM slot whose first channel is a surface when servo_hz is not 0.
uint8_t outputs_servo_rate_check (const outputs_t *o, uint16_t hz)
 Whether the SERVO page may take the frame rate hz against the bank o as it is: 0, or LINK_NACK_BAD_VALUE for a rate that is neither 0 nor in the PWM driver's range, or one that would leave a PWM slice asked for two rates – a surface it moves sharing a slice with an output it does not.
uint8_t outputs_chan_cfg_rate_check (const outputs_t *o, const uint16_t *chan_cfg, uint16_t servo_hz)
 Whether the bank o, given the CHAN_CFG page chan_cfg or the OUTPUTS page slots, would leave the SERVO page's rate servo_hz splitting a PWM slice: 0, or LINK_NACK_BAD_VALUE.
uint8_t outputs_slots_rate_check (const outputs_t *o, const uint16_t *slots, uint16_t servo_hz)
 The same as outputs_chan_cfg_rate_check(), for an OUTPUTS page.
static bool pulse_fits (const outputs_t *o, unsigned ch, uint16_t hz)
 Whether channel ch's longer endpoint fits a frame of hz.
uint8_t outputs_pulse_check (const outputs_t *o, uint16_t servo_hz, uint16_t channels, uint16_t channels_hz)
 Whether every pulse the bank o can render fits the frame that carries it: 0, or LINK_NACK_BAD_VALUE (protocol 4.16).
uint8_t outputs_chan_cfg_pulse_check (const outputs_t *o, const uint16_t *chan_cfg, const uint16_t *slots, uint16_t servo_hz)
 outputs_pulse_check() of the bank a write would leave, judged on a copy: chan_cfg applied, and slots too when it is not NULL – a BIND commit puts both pages in force at once, so its CHAN_CFG page is judged with its own OUTPUTS page. 0, or LINK_NACK_BAD_VALUE.
uint8_t outputs_slots_pulse_check (const outputs_t *o, const uint16_t *slots, uint16_t servo_hz)
 outputs_pulse_check() of the bank an OUTPUTS write would leave, judged on a copy.
unsigned outputs_slots_free_long (const outputs_t *o, uint16_t *slots)
 A stored OUTPUTS page made safe to restore under the channels of o: every PWM slot whose channel's longer endpoint does not fit the frame of the slot's own rate gets driver 0 in slots, and the count is returned.
uint8_t outputs_slots_changed (const uint16_t *prev, const uint16_t *next)
 The slots, one bit each, whose four OUTPUTS registers differ between prev and next.
uint8_t outputs_bind_check (const outputs_t *o, uint8_t watch, uint8_t bound_after)
 Whether an OUTPUTS write the bank has taken must be refused because the silicon did not bind it: LINK_NACK_BAD_VALUE when a slot of o with a driver is not set in bound_after and is set in watch, else 0. watch is the slots the write changed plus the slots that were bound before it, so a slot an earlier write or a restart left unbound does not refuse a write that never touched it. A watch of 0xFF covers every slot.
void outputs_slots_defaults (uint16_t *regs)
 Sets the OUTPUTS page to its defaults: every field zero, so no slot is configured.
uint8_t outputs_slots_write (uint16_t *regs, uint8_t off, uint8_t n, const uint16_t *in)
 Stores a window of the OUTPUTS page, refusing the whole window on an unknown driver or pin.
void outputs_slots_apply (outputs_t *o, const uint16_t *regs)
 Puts an OUTPUTS page into the bank: each configured slot is configured in turn, and a refused slot is left cleared.
void outputs_channels_defaults (uint16_t *regs)
 Sets the CHANNELS page to zero: every output asked for 0.
void outputs_channels_from_bank (const outputs_t *o, uint16_t *regs)
 The page as a mirror of the bank, for the two moments the bank is the authority and the page is not: at boot, and after a failsafe.
uint8_t outputs_channels_write (uint16_t *regs, uint8_t off, uint8_t n, const uint16_t *in)
 Stores a window of the CHANNELS page, clamping each command to LINK_CH_SPAN.
void outputs_channels_apply_n (outputs_t *o, const uint16_t *regs, uint8_t first, uint8_t count, uint32_t now_ms)
 Apply count channels of a CHANNELS page from first, as commands.
void outputs_channels_apply (outputs_t *o, const uint16_t *regs, uint32_t now_ms)
 Commands every channel of the CHANNELS page into the bank.
uint64_t outputs_role_pins (const uint16_t *slots, const uint16_t *chan_cfg, out_role_t role)
 The GPIOs of the slots that render a channel marked with role, as a mask: bit n is GPIO n.
uint8_t outputs_role_channels (const uint16_t *slots, const uint16_t *chan_cfg, out_role_t role)
 The channels these two pages render and mark with role, as a mask.
uint8_t outputs_arm_roles_of (uint16_t arm)
 The roles a CONTROL ARM value arms, as OUT_ROLE_BIT()s: OUT_ROLES_ALL for LINK_ARM_ALL, the named roles for a set of LINK_ARM_ROLES, and 0 for 0 and for a value the page refuses (link_arm_value_ok()).
uint16_t outputs_arm_value_of (uint8_t roles)
 The CONTROL ARM value that arms roles, OUT_ROLE_BIT()s: a set of LINK_ARM_ROLES, or 0 for no role.
uint16_t outputs_arm_value_for (uint16_t far_minor, uint8_t roles)
 The ARM value the panel writes to arm roles, OUT_ROLE_BIT()s, at an IOmcu of protocol minor far_minor: LINK_ARM_ALL below LINK_MINOR_ARM_ROLE, else outputs_arm_value_of(roles), and LINK_ARM_ALL when no role of OUT_ROLES_ALL is named.

Variables

static outputs_t s_trial
 The bank a page write would leave, judged on a copy: the bank itself is what drives, and a refused write must leave it as it was.

Detailed Description

The link's output pages, expressed as bank operations.

See outputs_pages.h.

SPDX-License-Identifier: MIT

Function Documentation

◆ driver_of()

out_driver_t driver_of ( uint16_t wire)
static

The wire's driver numbers are a contract, so they are mapped rather than cast: a reordered bank enum leaves this correct.

Parameters
wireThe LINK_DRIVER_* value from the wire.
Returns
The bank driver, OUT_DRIVER_NONE for a number this build does not know.

◆ known_driver()

bool known_driver ( uint16_t wire)
static

Whether a wire driver number is one this build knows.

Parameters
wireThe LINK_DRIVER_* value from the wire.
Returns
true for LINK_DRIVER_NONE up to LINK_DRIVER_DSHOT_BIDIR, false above.

◆ link_driver_of()

uint16_t link_driver_of ( out_driver_t d)

A bank driver as the wire numbers it.

The wire's numbers are a contract and the bank's enum is not, so the two are mapped rather than cast in both directions. Returns LINK_DRIVER_NONE for anything this build does not know.

Parameters
dThe bank driver.
Returns
The LINK_DRIVER_* wire number of d, LINK_DRIVER_NONE for a driver this build does not know.

◆ outputs_arm_roles_of()

uint8_t outputs_arm_roles_of ( uint16_t arm)

The roles a CONTROL ARM value arms, as OUT_ROLE_BIT()s: OUT_ROLES_ALL for LINK_ARM_ALL, the named roles for a set of LINK_ARM_ROLES, and 0 for 0 and for a value the page refuses (link_arm_value_ok()).

Parameters
armThe CONTROL ARM value.
Returns
The OUT_ROLE_BIT() set the value arms, 0 for a value refused.

◆ outputs_arm_value_for()

uint16_t outputs_arm_value_for ( uint16_t far_minor,
uint8_t roles )

The ARM value the panel writes to arm roles, OUT_ROLE_BIT()s, at an IOmcu of protocol minor far_minor: LINK_ARM_ALL below LINK_MINOR_ARM_ROLE, else outputs_arm_value_of(roles), and LINK_ARM_ALL when no role of OUT_ROLES_ALL is named.

Never 0.

Parameters
far_minorThe IOmcu's protocol minor version.
rolesThe roles to arm, OUT_ROLE_BIT() bits.
Returns
The ARM value to write. Never 0.

◆ outputs_arm_value_of()

uint16_t outputs_arm_value_of ( uint8_t roles)

The CONTROL ARM value that arms roles, OUT_ROLE_BIT()s: a set of LINK_ARM_ROLES, or 0 for no role.

An IOmcu older than LINK_MINOR_ARM_ROLE takes LINK_ARM_ALL and nothing narrower.

Parameters
rolesThe roles to arm, OUT_ROLE_BIT() bits.
Returns
The CONTROL ARM value, LINK_ARM_ROLES bits; 0 for no role.

◆ outputs_bind_check()

uint8_t outputs_bind_check ( const outputs_t * o,
uint8_t watch,
uint8_t bound_after )

Whether an OUTPUTS write the bank has taken must be refused because the silicon did not bind it: LINK_NACK_BAD_VALUE when a slot of o with a driver is not set in bound_after and is set in watch, else 0. watch is the slots the write changed plus the slots that were bound before it, so a slot an earlier write or a restart left unbound does not refuse a write that never touched it. A watch of 0xFF covers every slot.

Parameters
oThe bank.
watchBit n set for each slot to check.
bound_afterBit n set for each slot the silicon bound.
Returns
0 or LINK_NACK_BAD_VALUE. LINK_NACK_BAD_VALUE for NULL o.

◆ outputs_chan_cfg_apply()

void outputs_chan_cfg_apply ( outputs_t * o,
const uint16_t * regs )

Puts a CHAN_CFG page into the bank: role, slew and endpoints of each channel.

The endpoints are straightened and refused outside the floor and ceiling by the bank (outputs_set_endpoints()).

Parameters
oThe bank. Nothing is done when NULL.
regsThe CHAN_CFG page, LINK_CC_COUNT registers.

◆ outputs_chan_cfg_armed_check()

uint8_t outputs_chan_cfg_armed_check ( const uint16_t * regs,
const uint16_t * next,
bool armed )

Whether a CHAN_CFG or OUTPUTS write is refused because the bench is armed: 0, or LINK_NACK_BAD_VALUE, the reason the SENSE page gives for the same rule.

regs is the page in force and next the page as the write would leave it. Always 0 while armed is false.

OUTPUTS: any change. A slot rebound under a driving bank takes a pin away from an output, or hands one a pin, in the middle of a run.

CHAN_CFG: a change of any channel's role, and any change to a channel whose role is throttle – endpoints that move under a running motor move its throttle. A surface's slew and endpoints are taken armed: the SERVO screen states the range with every position it commands, and its SPEED is the slew.

A write of the page in force is taken on both.

Parameters
regsThe page in force.
nextThe page as the write would leave it.
armedWhether the bench is armed.
Returns
0 when the write is taken. LINK_NACK_BAD_VALUE when refused, and 0 while armed is false or a pointer is NULL.

◆ outputs_chan_cfg_defaults()

void outputs_chan_cfg_defaults ( uint16_t * regs)

Sets the CHAN_CFG page to its defaults: every channel a surface, slew 0, and the default endpoints.

Nothing is done when regs is NULL.

Parameters
regsThe CHAN_CFG page, LINK_CC_COUNT registers, overwritten for each of the LINK_OUT_CHANNELS channels.

◆ outputs_chan_cfg_pulse_check()

uint8_t outputs_chan_cfg_pulse_check ( const outputs_t * o,
const uint16_t * chan_cfg,
const uint16_t * slots,
uint16_t servo_hz )

outputs_pulse_check() of the bank a write would leave, judged on a copy: chan_cfg applied, and slots too when it is not NULL – a BIND commit puts both pages in force at once, so its CHAN_CFG page is judged with its own OUTPUTS page. 0, or LINK_NACK_BAD_VALUE.

Works on a static copy of the bank, so it is not reentrant.

Parameters
oThe bank.
chan_cfgThe CHAN_CFG page to apply, LINK_CC_COUNT registers.
slotsThe OUTPUTS page to apply, LINK_OS_COUNT registers, or NULL to leave the slots as they are.
servo_hzThe SERVO frame rate in Hz.
Returns
0 or LINK_NACK_BAD_VALUE. LINK_NACK_BAD_VALUE for NULL o or chan_cfg.

◆ outputs_chan_cfg_rate_check()

uint8_t outputs_chan_cfg_rate_check ( const outputs_t * o,
const uint16_t * chan_cfg,
uint16_t servo_hz )

Whether the bank o, given the CHAN_CFG page chan_cfg or the OUTPUTS page slots, would leave the SERVO page's rate servo_hz splitting a PWM slice: 0, or LINK_NACK_BAD_VALUE.

A role that stops being a surface, or a slot bound beside one, moves the rate the slice is asked for; the silicon would refuse whichever bound second and the page would read back a binding no pin has. Always 0 while servo_hz is 0.

Works on a static copy of the bank, so it is not reentrant.

Parameters
oThe bank.
chan_cfgThe CHAN_CFG page to judge, LINK_CC_COUNT registers.
servo_hzThe SERVO frame rate in Hz.
Returns
0 or LINK_NACK_BAD_VALUE. LINK_NACK_BAD_VALUE for NULL o or chan_cfg.

◆ outputs_chan_cfg_restore()

void outputs_chan_cfg_restore ( uint16_t * regs,
const uint16_t * saved )

The CHAN_CFG page saved, kept in flash, into regs at start.

A slew above LINK_CC_SLEW_MAX, which no write takes, becomes LINK_CC_SLEW_MAX; every other register is copied as kept. Nothing without both.

Parameters
regsReceives LINK_CC_COUNT registers.
savedThe kept page, LINK_CC_COUNT registers.

◆ outputs_chan_cfg_set_throttle_range()

bool outputs_chan_cfg_set_throttle_range ( uint16_t * regs,
uint16_t min_us,
uint16_t max_us )

Give every channel regs marks as a throttle the endpoints min_us and max_us, and leave every other channel as it is.

The bench's two pulse settings are a throttle's: an ESC (electronic speed controller) calibrated on a transmitter takes that transmitter's shortest pulse as zero. A surface keeps its own range – the schema's default, or the one the SERVO screen named for the servo on it – because a surface that took an ESC's range would rest at that range's midpoint, off centre whenever the range is not symmetric about 1500us.

Returns false and changes nothing for endpoints the page would refuse: below LINK_CC_FLOOR_US, above LINK_CC_CEILING_US, or not min_us below max_us. A refused write would take the roles down with it.

Parameters
regsThe CHAN_CFG page, LINK_CC_COUNT registers.
min_usEndpoint for command 0 of a throttle, in microseconds.
max_usEndpoint for OUT_SPAN of a throttle, in microseconds.
Returns
true when the throttle endpoints are set, false for NULL regs or endpoints refused as above.

◆ outputs_chan_cfg_write()

uint8_t outputs_chan_cfg_write ( uint16_t * regs,
uint8_t off,
uint8_t n,
const uint16_t * in )

Stores a window of the CHAN_CFG page, refusing the whole window on a bad value.

Parameters
regsThe CHAN_CFG page, LINK_CC_COUNT registers. Written only when the whole window is accepted.
offFirst register of the window.
nNumber of registers in the window.
inThe n register values.
Returns
0 when stored. LINK_NACK_BAD_RANGE for NULL pointers or a window past LINK_CC_COUNT. LINK_NACK_BAD_VALUE for a role above the surface value, a slew above LINK_CC_SLEW_MAX, or an endpoint outside LINK_CC_FLOOR_US to LINK_CC_CEILING_US.

◆ outputs_channels_apply()

void outputs_channels_apply ( outputs_t * o,
const uint16_t * regs,
uint32_t now_ms )

Commands every channel of the CHANNELS page into the bank.

Parameters
oThe bank. Nothing is done when NULL.
regsThe CHANNELS page, LINK_CH_COUNT registers.
now_msCurrent time in milliseconds, stamped on each channel.

◆ outputs_channels_apply_n()

void outputs_channels_apply_n ( outputs_t * o,
const uint16_t * regs,
uint8_t first,
uint8_t count,
uint32_t now_ms )

Apply count channels of a CHANNELS page from first, as commands.

A page write arrives in frames and each frame says which registers it carries, so only those are commanded: a channel outside the frame has not been commanded by it, and stamping its clock would keep it alive on somebody else's traffic. That is the whole of the per-channel timeout – a servo held at 10Hz must not keep a surface nobody is driving from reaching its rest.

Parameters
oThe bank.
regsThe CHANNELS page, indexed by channel number.
firstFirst channel to command, below LINK_CH_COUNT.
countNumber of channels from first. Clipped at LINK_CH_COUNT.
now_msCurrent time in milliseconds, stamped on each channel.

◆ outputs_channels_defaults()

void outputs_channels_defaults ( uint16_t * regs)

Sets the CHANNELS page to zero: every output asked for 0.

Nothing is done when regs is NULL.

Parameters
regsThe CHANNELS page, LINK_CH_COUNT registers, overwritten.

◆ outputs_channels_from_bank()

void outputs_channels_from_bank ( const outputs_t * o,
uint16_t * regs )

The page as a mirror of the bank, for the two moments the bank is the authority and the page is not: at boot, and after a failsafe.

Zero is not the answer at either – zero is a throttle's rest and a surface's low endpoint, so a zero-filled page applied as commands asks every surface for its endpoint. Filling the page from the bank leaves a channel nobody has commanded reading back at its role's rest, which is where it sits.

Parameters
oThe bank. Nothing is done when NULL.
regsReceives LINK_CH_COUNT commands, each outputs_command().

◆ outputs_channels_write()

uint8_t outputs_channels_write ( uint16_t * regs,
uint8_t off,
uint8_t n,
const uint16_t * in )

Stores a window of the CHANNELS page, clamping each command to LINK_CH_SPAN.

Parameters
regsThe CHANNELS page, LINK_CH_COUNT registers.
offFirst register of the window.
nNumber of registers in the window.
inThe n command values.
Returns
0 when stored. LINK_NACK_BAD_RANGE for NULL pointers or a window past LINK_CH_COUNT.

◆ outputs_pulse_check()

uint8_t outputs_pulse_check ( const outputs_t * o,
uint16_t servo_hz,
uint16_t channels,
uint16_t channels_hz )

Whether every pulse the bank o can render fits the frame that carries it: 0, or LINK_NACK_BAD_VALUE (protocol 4.16).

A pulse fits when it is no longer than out_pwm_pulse_max() of its frame rate: one microsecond less than the period, 1784us at 560Hz and 2499us at 400Hz. A longer one leaves the pin high for the whole frame. What is judged is a channel's longer endpoint, the pulse at full travel.

Each PWM slot is judged at the rate it runs at (outputs_slot_rates() with servo_hz), and each channel in channels – bit n is bank channel n, the programming ports whose pulse is on – at channels_hz. PPM and DShot slots are not judged: their frame is built by their driver.

Parameters
oThe bank.
servo_hzThe SERVO frame rate in Hz, 0 for the slots' own rates.
channelsBit n set for a bank channel whose pulse is judged.
channels_hzThe frame rate in Hz the channels in channels run at.
Returns
0 when every judged pulse fits. LINK_NACK_BAD_VALUE otherwise, and for NULL o.

◆ outputs_role_channels()

uint8_t outputs_role_channels ( const uint16_t * slots,
const uint16_t * chan_cfg,
out_role_t role )

The channels these two pages render and mark with role, as a mask.

Bit n is channel n. Read per channel from the pages themselves, because a binding cannot answer this: outbind_from_slots() reads a slot's role from its first channel and lets the rest differ, so a multi-channel slot whose CHAN_CFG roles disagree collapses to one of them. A screen that commands a role has to reach exactly the channels that carry it on the wire, and a servo horn reaching a channel bound as a throttle is a motor commanded to where a surface rests.

A channel no slot renders is never in the mask, so a command sent to what this returns always reaches a pin. Either page NULL returns zero, which is a caller that knows nothing rather than one that assumes.

One bit per channel over LINK_OUT_CHANNELS channels, which is 8.

Parameters
slotsThe OUTPUTS page, LINK_OS_COUNT registers.
chan_cfgThe CHAN_CFG page, LINK_CC_COUNT registers.
roleThe role to select.
Returns
Bit n set for channel n when a slot renders it and its CHAN_CFG role is role. 0 when either page is NULL.

◆ outputs_role_pins()

uint64_t outputs_role_pins ( const uint16_t * slots,
const uint16_t * chan_cfg,
out_role_t role )

The GPIOs of the slots that render a channel marked with role, as a mask: bit n is GPIO n.

The channels are those of outputs_role_channels(), so the pins named are the pins the SERVO screen's commands reach. A slot is one pin whatever its channel count. Either page NULL, or a pin of 64 or more, adds nothing.

Parameters
slotsThe OUTPUTS page, LINK_OS_COUNT registers.
chan_cfgThe CHAN_CFG page, LINK_CC_COUNT registers.
roleThe role to select.
Returns
Bit n set for GPIO n of each slot that renders a channel of role. 0 when either page is NULL.

◆ outputs_servo_rate_check()

uint8_t outputs_servo_rate_check ( const outputs_t * o,
uint16_t hz )

Whether the SERVO page may take the frame rate hz against the bank o as it is: 0, or LINK_NACK_BAD_VALUE for a rate that is neither 0 nor in the PWM driver's range, or one that would leave a PWM slice asked for two rates – a surface it moves sharing a slice with an output it does not.

Parameters
oThe bank.
hzThe frame rate in Hz, 0 for the slots' own rates.
Returns
0 when the rate is taken. LINK_NACK_BAD_VALUE otherwise, and for NULL o.

◆ outputs_slot_rates()

void outputs_slot_rates ( const outputs_t * o,
uint16_t servo_hz,
uint16_t * rate )

The frame rate each slot of o is bound at, into rate (OUT_MAX_SLOTS entries): the slot's own, or servo_hz for a PWM slot whose first channel is a surface when servo_hz is not 0.

A slot that is not PWM keeps its own rate, which for DShot is a bit rate.

Nothing is done when o or rate is NULL.

Parameters
oThe bank.
servo_hzThe SERVO frame rate in Hz, 0 for none.
rateReceives OUT_MAX_SLOTS rates in Hz.

◆ outputs_slots_apply()

void outputs_slots_apply ( outputs_t * o,
const uint16_t * regs )

Puts an OUTPUTS page into the bank: each configured slot is configured in turn, and a refused slot is left cleared.

Parameters
oThe bank. Nothing is done when NULL.
regsThe OUTPUTS page, LINK_OS_COUNT registers.

◆ outputs_slots_armed_check()

uint8_t outputs_slots_armed_check ( const uint16_t * regs,
const uint16_t * next,
bool armed )

The OUTPUTS write counterpart of outputs_chan_cfg_armed_check().

Parameters
regsThe OUTPUTS page in force, LINK_OS_COUNT registers.
nextThe OUTPUTS page as the write would leave it.
armedWhether the bench is armed.
Returns
LINK_NACK_BAD_VALUE for any change while armed, 0 otherwise or for NULL pointers.

◆ outputs_slots_changed()

uint8_t outputs_slots_changed ( const uint16_t * prev,
const uint16_t * next )

The slots, one bit each, whose four OUTPUTS registers differ between prev and next.

Parameters
prevThe OUTPUTS page before the write, LINK_OS_COUNT registers.
nextThe OUTPUTS page after the write, LINK_OS_COUNT registers.
Returns
Bit n set for slot n when its registers differ. 0xFF when either pointer is NULL.

◆ outputs_slots_defaults()

void outputs_slots_defaults ( uint16_t * regs)

Sets the OUTPUTS page to its defaults: every field zero, so no slot is configured.

Nothing is done when regs is NULL.

Parameters
regsThe OUTPUTS page, LINK_OS_COUNT registers, overwritten.

◆ outputs_slots_free_long()

unsigned outputs_slots_free_long ( const outputs_t * o,
uint16_t * slots )

A stored OUTPUTS page made safe to restore under the channels of o: every PWM slot whose channel's longer endpoint does not fit the frame of the slot's own rate gets driver 0 in slots, and the count is returned.

The SERVO page starts at 0, so a restart runs each slot at its own rate, and a slot stored by a build that took the combination – or taken under a SERVO rate that no longer holds – starts unbound rather than pinned high. The page reads back driver 0 for it.

Parameters
oThe bank, whose channels are judged.
slotsThe OUTPUTS page, LINK_OS_COUNT registers. Changed in place.
Returns
The number of slots whose driver was set to 0. 0 for NULL.

◆ outputs_slots_pulse_check()

uint8_t outputs_slots_pulse_check ( const outputs_t * o,
const uint16_t * slots,
uint16_t servo_hz )

outputs_pulse_check() of the bank an OUTPUTS write would leave, judged on a copy.

Works on a static copy of the bank, so it is not reentrant.

Parameters
oThe bank.
slotsThe OUTPUTS page to apply, LINK_OS_COUNT registers.
servo_hzThe SERVO frame rate in Hz.
Returns
0 or LINK_NACK_BAD_VALUE. LINK_NACK_BAD_VALUE for NULL o or slots.

◆ outputs_slots_rate_check()

uint8_t outputs_slots_rate_check ( const outputs_t * o,
const uint16_t * slots,
uint16_t servo_hz )

The same as outputs_chan_cfg_rate_check(), for an OUTPUTS page.

Works on a static copy of the bank, so it is not reentrant.

Parameters
oThe bank.
slotsThe OUTPUTS page to judge, LINK_OS_COUNT registers.
servo_hzThe SERVO frame rate in Hz.
Returns
0 or LINK_NACK_BAD_VALUE. LINK_NACK_BAD_VALUE for NULL o or slots.

◆ outputs_slots_write()

uint8_t outputs_slots_write ( uint16_t * regs,
uint8_t off,
uint8_t n,
const uint16_t * in )

Stores a window of the OUTPUTS page, refusing the whole window on an unknown driver or pin.

A pin conflict or a rate a driver cannot make is judged by outputs_configure() when the page is applied, not here.

Parameters
regsThe OUTPUTS page, LINK_OS_COUNT registers. Written only when the whole window is accepted.
offFirst register of the window.
nNumber of registers in the window.
inThe n register values.
Returns
0 when stored. LINK_NACK_BAD_RANGE for NULL pointers or a window past LINK_OS_COUNT. LINK_NACK_BAD_VALUE for a driver number this build does not know, or a pin above OUT_MAX_PIN.

◆ pulse_fits()

bool pulse_fits ( const outputs_t * o,
unsigned ch,
uint16_t hz )
static

Whether channel ch's longer endpoint fits a frame of hz.

The bank keeps the pair straightened, so max_us is the longer one.

Parameters
oThe bank.
chChannel index.
hzFrame rate in Hz.
Returns
true when ch is below OUT_MAX_CHANNELS and its max_us is at most out_pwm_pulse_max(hz).

◆ servo_rate_reaches()

bool servo_rate_reaches ( const outputs_t * o,
unsigned i )
static

Whether the SERVO page's rate, when not 0, is the one slot i runs at.

Parameters
oThe bank.
iSlot index, 0 to OUT_MAX_SLOTS - 1.
Returns
true for a PWM slot whose first channel is a surface, false otherwise.

Variable Documentation

◆ s_trial

outputs_t s_trial
static

The bank a page write would leave, judged on a copy: the bank itself is what drives, and a refused write must leave it as it was.

Static rather than on the stack, which on the IOmcu is the link task's.