|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
The link's three output pages (CHAN_CFG, OUTPUTS, CHANNELS), expressed as bank operations. More...
Go to the source code of this file.
Functions | |
| uint16_t | link_driver_of (out_driver_t d) |
| A bank driver as the wire numbers it. | |
| 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. | |
| 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. | |
| 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_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. | |
| 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. | |
| 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(). | |
| 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_apply (outputs_t *o, const uint16_t *regs) |
| Puts a CHAN_CFG page into the bank: role, slew and endpoints of each channel. | |
| void | outputs_chan_cfg_restore (uint16_t *regs, const uint16_t *saved) |
The CHAN_CFG page saved, kept in flash, into regs at start. | |
| 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_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. | |
| 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. | |
| 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. | |
| 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 (outputs_t *o, const uint16_t *regs, uint32_t now_ms) |
| Commands every channel of the CHANNELS page into the bank. | |
The link's three output pages (CHAN_CFG, OUTPUTS, CHANNELS), expressed as bank operations.
The mapping validates a driver number this build knows, an endpoint a servo can take and a channel range that fits. It is host-tested, and the IOmcu is wiring.
Each page has three entry points: _defaults puts the register array into the state an IOmcu holds before anybody has configured it. _write validates a register window and stores it, refusing atomically: a rejected write leaves the page as it was. _apply derives the bank from the whole stored page.
The register array is also what a read returns, so the page and the bank are two views kept from disagreeing.
SPDX-License-Identifier: MIT
| 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.
| d | The bank driver. |
d, LINK_DRIVER_NONE for a driver this build does not know. | 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()).
| arm | The CONTROL ARM value. |
| 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.
| far_minor | The IOmcu's protocol minor version. |
| roles | The roles to arm, OUT_ROLE_BIT() bits. |
| 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.
| roles | The roles to arm, OUT_ROLE_BIT() bits. |
| 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.
| o | The bank. |
| watch | Bit n set for each slot to check. |
| bound_after | Bit n set for each slot the silicon bound. |
o. | 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()).
| o | The bank. Nothing is done when NULL. |
| regs | The CHAN_CFG page, LINK_CC_COUNT registers. |
| 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.
| regs | The page in force. |
| next | The page as the write would leave it. |
| armed | Whether the bench is armed. |
armed is false or a pointer is NULL. | 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.
| regs | The CHAN_CFG page, LINK_CC_COUNT registers, overwritten for each of the LINK_OUT_CHANNELS channels. |
| 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.
| o | The bank. |
| chan_cfg | The CHAN_CFG page to apply, LINK_CC_COUNT registers. |
| slots | The OUTPUTS page to apply, LINK_OS_COUNT registers, or NULL to leave the slots as they are. |
| servo_hz | The SERVO frame rate in Hz. |
o or chan_cfg. | 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.
| o | The bank. |
| chan_cfg | The CHAN_CFG page to judge, LINK_CC_COUNT registers. |
| servo_hz | The SERVO frame rate in Hz. |
o or chan_cfg. | 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.
| regs | Receives LINK_CC_COUNT registers. |
| saved | The kept page, LINK_CC_COUNT registers. |
| 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.
| regs | The CHAN_CFG page, LINK_CC_COUNT registers. |
| min_us | Endpoint for command 0 of a throttle, in microseconds. |
| max_us | Endpoint for OUT_SPAN of a throttle, in microseconds. |
regs or endpoints refused as above. | 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.
| regs | The CHAN_CFG page, LINK_CC_COUNT registers. Written only when the whole window is accepted. |
| off | First register of the window. |
| n | Number of registers in the window. |
| in | The n register values. |
| 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.
| o | The bank. Nothing is done when NULL. |
| regs | The CHANNELS page, LINK_CH_COUNT registers. |
| now_ms | Current time in milliseconds, stamped on each channel. |
| 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.
| regs | The CHANNELS page, LINK_CH_COUNT registers, overwritten. |
| 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.
| o | The bank. Nothing is done when NULL. |
| regs | Receives LINK_CH_COUNT commands, each outputs_command(). |
| 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.
| regs | The CHANNELS page, LINK_CH_COUNT registers. |
| off | First register of the window. |
| n | Number of registers in the window. |
| in | The n command values. |
| 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.
| o | The bank. |
| servo_hz | The SERVO frame rate in Hz, 0 for the slots' own rates. |
| channels | Bit n set for a bank channel whose pulse is judged. |
| channels_hz | The frame rate in Hz the channels in channels run at. |
o. | 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.
| slots | The OUTPUTS page, LINK_OS_COUNT registers. |
| chan_cfg | The CHAN_CFG page, LINK_CC_COUNT registers. |
| role | The role to select. |
role. 0 when either page is NULL. | 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.
| slots | The OUTPUTS page, LINK_OS_COUNT registers. |
| chan_cfg | The CHAN_CFG page, LINK_CC_COUNT registers. |
| role | The role to select. |
role. 0 when either page is NULL. | 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.
| o | The bank. |
| hz | The frame rate in Hz, 0 for the slots' own rates. |
o. | 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.
| o | The bank. |
| servo_hz | The SERVO frame rate in Hz, 0 for none. |
| rate | Receives OUT_MAX_SLOTS rates in Hz. |
| 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.
| o | The bank. Nothing is done when NULL. |
| regs | The OUTPUTS page, LINK_OS_COUNT registers. |
| 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().
| regs | The OUTPUTS page in force, LINK_OS_COUNT registers. |
| next | The OUTPUTS page as the write would leave it. |
| armed | Whether the bench is armed. |
armed, 0 otherwise or for NULL pointers. | 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.
| prev | The OUTPUTS page before the write, LINK_OS_COUNT registers. |
| next | The OUTPUTS page after the write, LINK_OS_COUNT registers. |
| 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.
| regs | The OUTPUTS page, LINK_OS_COUNT registers, overwritten. |
| 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.
| o | The bank, whose channels are judged. |
| slots | The OUTPUTS page, LINK_OS_COUNT registers. Changed in place. |
| 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.
| o | The bank. |
| slots | The OUTPUTS page to apply, LINK_OS_COUNT registers. |
| servo_hz | The SERVO frame rate in Hz. |
o or slots. | 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.
| o | The bank. |
| slots | The OUTPUTS page to judge, LINK_OS_COUNT registers. |
| servo_hz | The SERVO frame rate in Hz. |
o or slots. | 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.
| regs | The OUTPUTS page, LINK_OS_COUNT registers. Written only when the whole window is accepted. |
| off | First register of the window. |
| n | Number of registers in the window. |
| in | The n register values. |