|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
The bank rendered onto pins. More...
#include "outputs_hw.h"#include <string.h>#include "pico/stdlib.h"#include "dshot.h"#include "link_pages.h"#include "out_dshot.h"#include "out_ppm.h"#include "out_pwm.h"#include "port_park.h"#include "ppm.h"Data Structures | |
| struct | slot_state_t |
| Render state of one slot: its DShot schedule, extended telemetry and the last PWM pulse. More... | |
| struct | port_state_t |
| The programming ports: pins of their own on the same generator, each rendered from one channel of the bank. More... | |
| struct | telem_t |
| The last readings an ESC gave, with the clock each arrived on. More... | |
Macros | |
| #define | DSHOT_UPDATE_HZ 1000u |
| How often an ESC (electronic speed controller) is told what to do. | |
| #define | DSHOT_PERIOD_US (1000000u / DSHOT_UPDATE_HZ) |
Functions | |
| static void | ports_release_dshot (void) |
| Every DShot port let go: no frame, its state machine free, held again by the next outputs_hw_ports(). | |
| static uint32_t | now_ms (void) |
| The millisecond clock the telemetry ages and the ask schedule run on. | |
| static int | reporting_slot (void) |
| The slot whose readings the bench publishes: the lowest-numbered bound bidirectional output. | |
| static void | forget_telem (void) |
| Drop everything an ESC said. | |
| static void | forget_slot_telem (unsigned slot) |
| The same, for one slot: what it is bound to has changed, so what its ESC said belongs to an output that is no longer there. | |
| void | outputs_hw_init (void) |
| Forget every binding. | |
| bool | outputs_hw_bound (uint8_t slot) |
Whether slot slot is configured and bound to real silicon. | |
| static bool | same (const out_slot_t *a, const out_slot_t *b) |
| Tells whether two slot configurations bind the same way. | |
| static void | unbind (const out_slot_t *s) |
| Releases the pin of a slot from its driver. | |
| static bool | bind (const out_slot_t *s) |
| Binds a slot's pin to its driver. | |
| void | outputs_hw_apply (const outputs_t *o, const uint16_t *rate_hz) |
Make the bindings match the bank's slot table, each slot at the rate in rate_hz (OUT_MAX_SLOTS entries, from outputs_slot_rates()) rather than its own. | |
| void | outputs_hw_apply_only (const outputs_t *o, const uint16_t *rate_hz, uint8_t may_bind) |
As outputs_hw_apply(), with only the slots in may_bind bound; a slot outside it is released and left unbound. | |
| static void | record_telem (unsigned slot, const dshot_telem_t *t) |
| One reply, filed under what it turned out to be. | |
| static void | service_ppm (const outputs_t *o, const out_slot_t *s, bool drive) |
| Renders one PPM slot for this pass. | |
| static void | service_dshot (const outputs_t *o, const out_slot_t *s, slot_state_t *st, unsigned slot, bool drive) |
| Renders one DShot slot or port for this pass. | |
| void | outputs_hw_ports (const uint8_t *pin, const uint16_t *rate_hz, uint8_t parked, uint8_t dshot, uint8_t channel0) |
The programming ports' pins: pin[n] for port n, LINK_PG_PORTS_MAX entries, a value above OUT_MAX_PIN for a port without one. | |
| uint8_t | outputs_hw_ports_bound (void) |
| The ports whose function holds their pin, bit n for port n: bound to the generator or to DShot, or parked. | |
| void | outputs_hw_ports_drive (uint8_t ports) |
| Which bound ports render their channel's pulse while the bank drives, bit n for port n. | |
| void | outputs_hw_watch (int ch) |
Watch output channel ch for a move capture's edge; a negative ch stops watching. | |
| bool | outputs_hw_edge (uint64_t *us) |
The edge stamped since the last call, once: true with its time in us. | |
| static void | service_pwm (slot_state_t *st, const out_slot_t *s, uint16_t pulse) |
| A PWM slot's pulse for this pass. | |
| static bool | slot_driving (const outputs_t *o, const out_slot_t *s) |
Whether the bank is armed for the role of a channel of s, the slot as this layer bound it. | |
| void | outputs_hw_service (const outputs_t *o) |
| Render the bank onto the pins. | |
| bool | outputs_hw_erpm (uint32_t *erpm, uint32_t *age_ms) |
| The last electrical rpm a bidirectional DShot ESC (electronic speed controller) reported, and how long ago in milliseconds. | |
| bool | outputs_hw_edt (dshot_telem_kind_t kind, uint16_t *value, uint32_t *age_ms) |
The last extended-telemetry reading of kind, and how long ago in milliseconds. | |
Variables | |
| static out_slot_t | s_shadow [OUT_MAX_SLOTS] |
| static slot_state_t | s_state [OUT_MAX_SLOTS] |
| static port_state_t | s_port [LINK_PG_PORTS_MAX] |
| static uint8_t | s_port_drive |
| The ports that render their pulse (outputs_hw_ports_drive()). | |
| static int | s_watch_ch = -1 |
| The capture's edge: the output channel watched, -1 for none, and the frame stamped for it, until taken. | |
| static bool | s_edge_ready |
| static uint64_t | s_edge_us |
| static telem_t | s_telem [OUT_MAX_SLOTS] |
| #define DSHOT_UPDATE_HZ 1000u |
How often an ESC (electronic speed controller) is told what to do.
The OUTPUTS page's rate field is a bit rate for DShot, not a frame rate, so this is the one output timing the wire does not carry. A kilohertz is twenty times the rate the panel polls at and far inside every ESC's own signal timeout, and for bidirectional DShot it is also the telemetry rate: a thousand speed readings a second against a bench that samples fifty.
|
static |
Binds a slot's pin to its driver.
| s | The slot to bind. |
|
static |
The same, for one slot: what it is bound to has changed, so what its ESC said belongs to an output that is no longer there.
| slot | The slot. Ignored when out of range. |
|
static |
Drop everything an ESC said.
At the end of a run, because the next run need not be the same ESC: one can be unplugged and another fitted between two arms, and a reading that outlived its sender would be published as the new one's for as long as its staleness window lasts – and would seed that run's peaks on the way past.
| void outputs_hw_apply | ( | const outputs_t * | o, |
| const uint16_t * | rate_hz ) |
Make the bindings match the bank's slot table, each slot at the rate in rate_hz (OUT_MAX_SLOTS entries, from outputs_slot_rates()) rather than its own.
Call after anything that changes a slot, a role or a rate. A slot that has not changed is left alone rather than rebuilt, so reconfiguring one output does not interrupt another; a slot the silicon cannot serve is left unbound, and outputs_hw_bound() says so.
| o | The output bank. |
| rate_hz | OUT_MAX_SLOTS entries, from outputs_slot_rates(). |
| void outputs_hw_apply_only | ( | const outputs_t * | o, |
| const uint16_t * | rate_hz, | ||
| uint8_t | may_bind ) |
As outputs_hw_apply(), with only the slots in may_bind bound; a slot outside it is released and left unbound.
A refused OUTPUTS write puts back exactly the set that was bound before it, so a slot unbound then cannot take the resource a slot bound then needs. When a slot that runs on PIO (DShot, PPM) is bound anew, every DShot programming port is let go first, so the slots take the state machines before the ports; the caller binds the ports again with outputs_hw_ports() after.
| o | The output bank. |
| rate_hz | OUT_MAX_SLOTS entries, from outputs_slot_rates(). |
| may_bind | Bit mask of the slots that may be bound. The others are released and left unbound. |
| bool outputs_hw_bound | ( | uint8_t | slot | ) |
Whether slot slot is configured and bound to real silicon.
| slot | Slot index. |
| bool outputs_hw_edge | ( | uint64_t * | us | ) |
The edge stamped since the last call, once: true with its time in us.
| us | Receives the time of the edge, in microseconds. |
| bool outputs_hw_edt | ( | dshot_telem_kind_t | kind, |
| uint16_t * | value, | ||
| uint32_t * | age_ms ) |
The last extended-telemetry reading of kind, and how long ago in milliseconds.
Raw: the payload byte as the ESC sent it, because the units belong to the frame type and not to this file. Returns false for a kind no reply has carried, which is every kind on an ESC that does not do extended telemetry and all of them until one has been asked and has answered.
Each kind carries its own age. They do not arrive together: speed comes back on every frame and the rest are interleaved between them.
| kind | The telemetry frame type. |
| value | Receives the payload byte, as the ESC sent it. |
| age_ms | Receives the time since the reading, in milliseconds. |
kind has been received. False otherwise. | bool outputs_hw_erpm | ( | uint32_t * | erpm, |
| uint32_t * | age_ms ) |
The last electrical rpm a bidirectional DShot ESC (electronic speed controller) reported, and how long ago in milliseconds.
Electrical rather than mechanical: an ESC reports periods and has no idea what it is bolted to, and the pole count arrives separately. Returns false when no ESC has ever answered.
| erpm | Receives the electrical rpm. |
| age_ms | Receives the time since the reading, in milliseconds. |
| void outputs_hw_init | ( | void | ) |
Forget every binding.
Call once, before the first apply.
| void outputs_hw_ports | ( | const uint8_t * | pin, |
| const uint16_t * | rate_hz, | ||
| uint8_t | parked, | ||
| uint8_t | dshot, | ||
| uint8_t | channel0 ) |
The programming ports' pins: pin[n] for port n, LINK_PG_PORTS_MAX entries, a value above OUT_MAX_PIN for a port without one.
A port in parked (bit n, FUNC NONE) holds its pin as an input with its pull-down and renders nothing. A port in dshot (bit n, FUNC DSHOT150 to DSHOT600) is bound to a DShot state machine at rate_hz[n] kbit/s and rendered as a plain DShot slot is: a frame every 1ms while the bank is armed for its channel's role, the throttle, and none while it is not. Any other port's pin is bound to the PWM generator at rate_hz[n] frames a second (SERVO PWM). A port whose pin, rate or function moved is released and held again; one that did not is left running. A port is no slot: it is rendered from bank channel channel0 + n. A pin the chip does not have, or a DShot pin no state machine is left for, is not held.
| pin | Pin of each port, one entry per port. |
| rate_hz | Rate of each port: kbit/s for DShot, frames per second for PWM. |
| parked | Bit n set when port n is parked. |
| dshot | Bit n set when port n runs DShot. |
| channel0 | Bank channel of port 0. |
| uint8_t outputs_hw_ports_bound | ( | void | ) |
The ports whose function holds their pin, bit n for port n: bound to the generator or to DShot, or parked.
| void outputs_hw_ports_drive | ( | uint8_t | ports | ) |
Which bound ports render their channel's pulse while the bank drives, bit n for port n.
A port left out is written a pulse of 0, as a disarmed bank writes it: the pulse in progress completes and the pin rests low. None until told.
| ports | Bit n set for port n. |
| void outputs_hw_service | ( | const outputs_t * | o | ) |
Render the bank onto the pins.
Call every pass.
Nothing edges unless outputs_driving() is true. DShot has a send rate of its own, because its frame rate is not the bank's business and the OUTPUTS page's rate field is a bit rate for it rather than a frame rate.
| o | The output bank. |
| void outputs_hw_watch | ( | int | ch | ) |
Watch output channel ch for a move capture's edge; a negative ch stops watching.
The first later pass that renders a pulse other than the one before it on ch's PWM slot or programming port writes it stamped (out_pwm_write_stamped()) and ends the watch: the start of the frame that first carries the new pulse, on time_us_64(). A pulse of 0, the bank stopping, is not an edge. A slot that is not PWM never stamps.
| ch | Output channel to watch, or a negative value to stop watching. |
|
static |
One reply, filed under what it turned out to be.
Each kind keeps its own arrival clock because they do not arrive at one rate: speed comes back on every frame and the extended kinds are interleaved between them, a few a second at most.
| slot | The slot the reply came from. Ignored when out of range. |
| t | The reply. |
|
static |
The slot whose readings the bench publishes: the lowest-numbered bound bidirectional output.
Defined rather than "whichever answered last", so the numbers on the screen belong to one motor and stay with it. A bench running two ESCs reports the first; the page carries one set of readings and choosing between them here is the only place that choice can be made once.
|
static |
Tells whether two slot configurations bind the same way.
| a | One slot. |
| b | The other slot. |
|
static |
Renders one DShot slot or port for this pass.
| o | The output bank. |
| s | The slot or port, bound as DShot. |
| st | Its render state. |
| slot | Its slot index, or OUT_MAX_SLOTS for a programming port. |
| drive | True while the bank is armed for its channel. |
|
static |
Renders one PPM slot for this pass.
| o | The output bank. |
| s | The slot, bound as PPM. |
| drive | True to write the channels' pulses, false to stop the pin. |
|
static |
A PWM slot's pulse for this pass.
On the watched channel, the first pulse other than the last one rendered – a changed command, not a disarm – is written stamped: the start of the frame that first carries it is the capture's edge. Once: the watch ends with it, stamped or not.
| st | The render state of the slot or port. |
| s | The slot or port, bound as PWM. |
| pulse | Pulse width in microseconds. 0 stops the pin edging. |
|
static |
Whether the bank is armed for the role of a channel of s, the slot as this layer bound it.
The shadow rather than the bank's slot: the shadow is what the pin is doing.
| o | The output bank. |
| s | The slot, from s_shadow. |
|
static |
Releases the pin of a slot from its driver.
| s | The slot whose pin is released. |