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

The SENSE, SERVO_SENSE and SERVO_WIN link pages at the IOmcu: the I2C bus of the two current monitors and the output encoder, its set-up and what they last read, the servo rail's channels with a move capture, and CH1's last four windows. More...

#include <stdbool.h>
#include <stdint.h>
#include "bench_state.h"
#include "link_pages.h"
#include "outputs.h"
#include "sense_svc.h"

Go to the source code of this file.

Data Structures

struct  sense_page_t
 The state of the SENSE page: its three register images, the generations, the pins and the map and limit registers. More...

Macros

#define SENSE_DEFAULT_SDA   16u
 The set-up before anything is written: both parts off; GP16 and GP17, I2C0's SDA and SCL, outside GP0 to GP7 where the guides put outputs; 400kHz; the MATEK I2C-INA-BM's INA228 at 0x45 on its 200 uOhm shunt, ranged for 204.8A; the DAOKAI INA3221 at 0x40 on its 0.1Ohm shunts (1.638A full scale), reading CH1.
#define SENSE_DEFAULT_SCL   17u
#define SENSE_DEFAULT_KHZ   LINK_SN_KHZ_BUS
#define SENSE_DEFAULT_I228_ADDR   0x45u
#define SENSE_DEFAULT_I228_UOHM   200u
#define SENSE_DEFAULT_I228_DA   2048u
#define SENSE_DEFAULT_I3221_ADDR   0x40u
#define SENSE_DEFAULT_I3221_DMOHM   1000u
#define SENSE_DEFAULT_I3221_CH   0x01u
#define SENSE_DEFAULT_MOVE_MA   100u
 A capture armed before anything else is written: 100mA of movement and a 50mA band, the servo test's SERVO_TEST_MOVE_A and SERVO_TEST_BAND_A.
#define SENSE_DEFAULT_BAND_MA   50u

Functions

void sense_page_init (sense_page_t *p)
 Set a SENSE page to its start-up state.
void sense_page_defaults (uint16_t *cfg)
 The set-up as a page starts, into cfg (LINK_SN_CONFIG_COUNT registers): what a store record from before protocol 4.7 restores.
uint32_t sense_i3221_full_scale_ma (uint16_t shunt_dmohm)
 The INA3221's full scale in mA for a shunt of shunt_dmohm tenths of a milliohm: 163.8mV across it. 1638 at 0.1Ohm. 0 for no shunt.
uint8_t sense_page_write (sense_page_t *p, uint8_t off, uint8_t n, const uint16_t *in, const outputs_t *o, uint64_t taken)
 A SENSE write, validated whole before any of it is stored.
void sense_page_read (const sense_page_t *p, uint8_t off, uint8_t n, uint16_t *out)
 Read SENSE registers into out.
uint8_t sense_servo_write (sense_page_t *p, uint8_t off, uint8_t n, const uint16_t *in, const outputs_t *o)
 A SERVO_SENSE write.
void sense_page_bound (sense_page_t *p, uint8_t slots)
 Which output slots the silicon bound (bit n slot n): a capture arms only on a PWM slot that renders frames.
void sense_page_ports (sense_page_t *p, uint8_t ports)
 Which programming ports render a pulse while the bank drives (bit n port n): bound to the PWM generator, switched on in LINK_PG_PWM and not held by the KST port.
bool sense_page_port_gone (const sense_page_t *p)
 Whether the capture in CAP_ARM names a programming port that renders no pulse now: its PWM switched off, its pin gone, or the KST port on the pin.
void sense_servo_read (const sense_page_t *p, uint8_t off, uint8_t n, uint16_t *out)
 Read SERVO_SENSE registers into out.
void sense_win_read (const sense_page_t *p, uint8_t off, uint8_t n, uint16_t *out)
 A SERVO_WIN read: CH1's ring as last published, and the capture's state, count and clipped bit as SERVO_SENSE reads them at this moment. The page takes no write.
bool sense_page_step (sense_page_t *p, bool driving)
 One pass: a bank that is not driving ends a capture that has not finished – CAP_ARM 0, CAP_STATE idle – and returns true.
void sense_page_cmd (const sense_page_t *p, sense_cmd_t *cmd)
 The order for core 1 from the page: the set-up and its generation, and the capture's.
void sense_page_publish (sense_page_t *p, const sense_snap_t *s, uint16_t run_gen)
 What core 1 read into the read-only registers of both pages, when s was taken under the set-up in force.
void sense_page_esc (sense_page_t *p, bool v_ok, float volts, bool i_ok, float amps)
 The ESC's own telemetry voltage and current, each with its valid bit, into SENSE registers 23 to 25.
void sense_page_bench (sense_page_t *p, const sense_snap_t *s, uint16_t run_gen, bool driving, bench_state_t *b)
 The BENCH page's numbers from the INA228, over what the ESC's telemetry put in b, once the peaks of b are tracked.
uint16_t sense_page_caps (const sense_page_t *p)
 The capability bits the set-up makes true: LINK_CAP_PACK_SENSE while the INA228 is enabled, LINK_CAP_SERVO_SENSE while the INA3221 is – fitted as configured, not online now – or, with SENSE_MAP in force, while a channel it reads watches an output channel or a programming port (sense_map.h).
bool sense_page_enabled (const sense_page_t *p)
 Whether either part is enabled, and so the bus runs.
uint8_t sense_page_sda (const sense_page_t *p)
 The bus's pins and clock.
uint8_t sense_page_scl (const sense_page_t *p)
 The SCL pin of the bus.
uint32_t sense_page_hz (const sense_page_t *p)
 The bus clock in Hz.
uint64_t sense_page_pins (const sense_page_t *p)
 The pins the set-up holds, as a reservation mask; 0 while neither part is enabled.
uint64_t sense_page_held (const sense_page_t *p)
 Those and the pins core 1 has not let go of yet (sense_page_t.held): a bus moved or closed keeps its old pins for about 1ms.
uint8_t sense_page_slots_check (const sense_page_t *p, const uint16_t *slots)
 LINK_NACK_BAD_VALUE for an OUTPUTS page (slots, LINK_OS_COUNT registers) that binds a slot to a pin sense_page_held() names, the refusal the SUPPLY page gives for its own; 0 otherwise.

Detailed Description

The SENSE, SERVO_SENSE and SERVO_WIN link pages at the IOmcu: the I2C bus of the two current monitors and the output encoder, its set-up and what they last read, the servo rail's channels with a move capture, and CH1's last four windows.

The registers are in link_pages.h (LINK_SN_*, LINK_SS_*, LINK_SW_*).

The set-up is refused while the bank is armed, on pins that are not one I2C block's SDA and SCL, and on pins the board, an output or the SUPPLY page already holds; the pins it takes are reserved from the outputs for as long as any part is enabled (sense_page_pins()). A capture arms only on an armed bank, on INA3221 CH1 while the INA3221 reads it, and for an output channel that is a surface on a PWM slot.

Host-tested. The page holds the contract and the checks. Reading the parts is core 1's (sense_svc.h); the page turns core 0's view of the page into the order core 1 runs (sense_page_cmd()), and what core 1 read into the read-only registers (sense_page_publish()) and into the BENCH page's numbers (sense_page_bench()). Until a snapshot of the set-up in force arrives the read-only registers read 0, FLAGS included: no bus open.

Generations. cfg_gen moves with every change of the set-up, cap_gen with every arm, every disarm and every capture a stopped bank ends. A snapshot taken under an earlier set-up publishes nothing but the pins core 1 still holds (sense_page_held()); one taken under an earlier capture order leaves the capture's registers as the page set them, so CAP_STATE never steps back from armed to the last capture's result.

SPDX-License-Identifier: MIT

Function Documentation

◆ sense_i3221_full_scale_ma()

uint32_t sense_i3221_full_scale_ma ( uint16_t shunt_dmohm)

The INA3221's full scale in mA for a shunt of shunt_dmohm tenths of a milliohm: 163.8mV across it. 1638 at 0.1Ohm. 0 for no shunt.

Parameters
shunt_dmohmThe shunt in tenths of a milliohm.
Returns
The full scale in mA; 0 for a shunt of 0.

◆ sense_page_bench()

void sense_page_bench ( sense_page_t * p,
const sense_snap_t * s,
uint16_t run_gen,
bool driving,
bench_state_t * b )

The BENCH page's numbers from the INA228, over what the ESC's telemetry put in b, once the peaks of b are tracked.

The INA228 is BENCH's source (LINK_BN_SENSED) while it is enabled and online, and stays it to the end of a run it was online in: a part that drops out while driving leaves voltage, current and power empty, not the ESC's. Voltage and current are the last 50ms window's means, each valid with a sample in it; power is their product, valid with both. The peaks are run run_gen's, from the 500Hz samples, and until core 1 has started that run, the live readings. Charge and energy are the part's ENERGY and CHARGE, with LINK_BN_TOTALS_OK, while they are the run's totals; otherwise 0 and the bit clear. Nothing changes while the INA228 is not BENCH's source.

Parameters
pThe page.
sThe snapshot core 1 took.
run_genThe generation of the current run.
drivingtrue while the bank drives.
bThe BENCH state, updated in place.

◆ sense_page_bound()

void sense_page_bound ( sense_page_t * p,
uint8_t slots )

Which output slots the silicon bound (bit n slot n): a capture arms only on a PWM slot that renders frames.

The IOmcu says so after every change of the bindings; until it does, no slot counts as bound.

Parameters
pThe page.
slotsBit n: output slot n is bound to silicon.

◆ sense_page_caps()

uint16_t sense_page_caps ( const sense_page_t * p)

The capability bits the set-up makes true: LINK_CAP_PACK_SENSE while the INA228 is enabled, LINK_CAP_SERVO_SENSE while the INA3221 is – fitted as configured, not online now – or, with SENSE_MAP in force, while a channel it reads watches an output channel or a programming port (sense_map.h).

They change only with a SENSE or SENSE_MAP write taken, because a panel reads the identity page at link-up and again after such a write, not on every poll; whether a part answers is FLAGS' and BENCH bit 5's, which it polls.

Parameters
pThe page, or NULL.
Returns
The capability bits; 0 for a NULL p.

◆ sense_page_cmd()

void sense_page_cmd ( const sense_page_t * p,
sense_cmd_t * cmd )

The order for core 1 from the page: the set-up and its generation, and the capture's.

The INA228's maximum in mA, the INA3221's shunt in µΩ; a capture's level before the command left to CH1 (SENSE_CAP_RISE_AUTO). run_gen, edge_set and edge_us are the caller's, and left as they are.

Parameters
pThe page.
cmdReceives the order for core 1.

◆ sense_page_defaults()

void sense_page_defaults ( uint16_t * cfg)

The set-up as a page starts, into cfg (LINK_SN_CONFIG_COUNT registers): what a store record from before protocol 4.7 restores.

Parameters
cfgReceives LINK_SN_CONFIG_COUNT registers, zeroed first; NULL is ignored.

◆ sense_page_enabled()

bool sense_page_enabled ( const sense_page_t * p)

Whether either part is enabled, and so the bus runs.

Parameters
pThe page, or NULL.
Returns
true when either part is enabled; false for a NULL p.

◆ sense_page_esc()

void sense_page_esc ( sense_page_t * p,
bool v_ok,
float volts,
bool i_ok,
float amps )

The ESC's own telemetry voltage and current, each with its valid bit, into SENSE registers 23 to 25.

Parameters
pThe page, updated in place.
v_oktrue when volts is valid.
voltsThe ESC voltage in V.
i_oktrue when amps is valid.
ampsThe ESC current in A.

◆ sense_page_held()

uint64_t sense_page_held ( const sense_page_t * p)

Those and the pins core 1 has not let go of yet (sense_page_t.held): a bus moved or closed keeps its old pins for about 1ms.

What no output may have.

Parameters
pThe page, or NULL.
Returns
The pins of sense_page_pins() and those core 1 has not let go of; 0 for a NULL p.

◆ sense_page_hz()

uint32_t sense_page_hz ( const sense_page_t * p)

The bus clock in Hz.

Parameters
pThe page, or NULL.
Returns
The clock in Hz (the set-up's kHz times 1000); 0 for a NULL p.

◆ sense_page_init()

void sense_page_init ( sense_page_t * p)

Set a SENSE page to its start-up state.

Parameters
pThe page; zeroed, then set to the defaults.

◆ sense_page_pins()

uint64_t sense_page_pins ( const sense_page_t * p)

The pins the set-up holds, as a reservation mask; 0 while neither part is enabled.

Parameters
pThe page, or NULL.
Returns
Bit n set for pin n of SDA and SCL; 0 while neither part is enabled or for a NULL p.

◆ sense_page_port_gone()

bool sense_page_port_gone ( const sense_page_t * p)

Whether the capture in CAP_ARM names a programming port that renders no pulse now: its PWM switched off, its pin gone, or the KST port on the pin.

The caller ends such a capture as it ends one whose bank stopped driving, with sense_page_step(p, false).

Parameters
pThe page, or NULL.
Returns
true when the capture names a programming port that renders no pulse; false for NULL p.

◆ sense_page_ports()

void sense_page_ports ( sense_page_t * p,
uint8_t ports )

Which programming ports render a pulse while the bank drives (bit n port n): bound to the PWM generator, switched on in LINK_PG_PWM and not held by the KST port.

A capture with LINK_SS_ARM_PORT arms only on one of them. Until told, none.

Parameters
pThe page.
portsBit n: programming port n renders a pulse.

◆ sense_page_publish()

void sense_page_publish ( sense_page_t * p,
const sense_snap_t * s,
uint16_t run_gen )

What core 1 read into the read-only registers of both pages, when s was taken under the set-up in force.

SENSE: FLAGS, PRESENT, the IDs, ERRORS, the die temperature, DIAG_ALRT, the charge and energy while they are the totals of run run_gen (sense_run_t.totals_ok), 0 otherwise, and RESETS. SERVO_SENSE: each channel's last window and its flags, the window number, and the capture when s was taken under the capture order in force. SERVO_WIN: CH1's ring, a clipped sample counted at the end of the range it read. Values are rounded to their register's step and held to its range.

FLAGS bit 3 (LINK_SN_I228_CLIPPED) is set when the INA228's last window held a clipped current, or the run's peaks lack one.

Parameters
pThe page, updated in place.
sThe snapshot core 1 took.
run_genThe generation of the current run.

◆ sense_page_read()

void sense_page_read ( const sense_page_t * p,
uint8_t off,
uint8_t n,
uint16_t * out )

Read SENSE registers into out.

Parameters
pThe page.
offFirst register of the window.
nNumber of registers to read.
outReceives n register values; left unchanged for a NULL p or out, or a window past LINK_SN_COUNT.

◆ sense_page_scl()

uint8_t sense_page_scl ( const sense_page_t * p)

The SCL pin of the bus.

Parameters
pThe page, or NULL.
Returns
The SCL pin number; 0 for a NULL p.

◆ sense_page_sda()

uint8_t sense_page_sda ( const sense_page_t * p)

The bus's pins and clock.

The SDA pin of the bus.

Parameters
pThe page, or NULL.
Returns
The SDA pin number; 0 for a NULL p.

◆ sense_page_slots_check()

uint8_t sense_page_slots_check ( const sense_page_t * p,
const uint16_t * slots )

LINK_NACK_BAD_VALUE for an OUTPUTS page (slots, LINK_OS_COUNT registers) that binds a slot to a pin sense_page_held() names, the refusal the SUPPLY page gives for its own; 0 otherwise.

Taken instead, such a slot would be left unbound with nothing on the wire to say so.

Parameters
pThe page, or NULL.
slotsThe OUTPUTS slot registers, LINK_OUT_SLOTS slots of LINK_OS_STRIDE registers each.
Returns
0, or LINK_NACK_BAD_VALUE for a NULL slots or a slot bound to a held pin.

◆ sense_page_step()

bool sense_page_step ( sense_page_t * p,
bool driving )

One pass: a bank that is not driving ends a capture that has not finished – CAP_ARM 0, CAP_STATE idle – and returns true.

A finished capture keeps its result until the next arm.

Parameters
pThe page.
drivingtrue while the bank drives.
Returns
true when an unfinished capture was ended; false otherwise or for NULL p.

◆ sense_page_write()

uint8_t sense_page_write ( sense_page_t * p,
uint8_t off,
uint8_t n,
const uint16_t * in,
const outputs_t * o,
uint64_t taken )

A SENSE write, validated whole before any of it is stored.

Refused: off the page (BAD_RANGE); a read-only register (READ_ONLY); a value out of its range, a reserved register written other than 0, SDA and SCL not one I2C block's pair (link_sn_pins_pair()) whatever is enabled – but for nothing enabled with SDA or SCL 0, a panel's pins that are not set – and while a part is enabled a pin past the bank, reserved, bound to an output or in taken, the two parts on one address while both are enabled, the INA3221 enabled with no channel, an INA228 shunt and maximum ina228_calibrate() refuses (past 163.84mV across the shunt, or a full scale past INA228_FS_MA_LIMIT at the range it chooses), and any change while o is driving (BAD_VALUE). A write of the set-up in force is taken armed or not.

The INA228's calibration is the driver's, ina228_calibrate(), so the page refuses exactly the set-ups the driver could not run.

taken is the pins another page holds: the SUPPLY page's, supply_page_pins(). The bank's reservation covers them on the IOmcu as well; they are named here so the page refuses them by itself.

A set-up taken clears every register core 1 fills – FLAGS, PRESENT, the IDs, ERRORS, the readings, the SERVO_SENSE windows and a finished capture's result, CAP_ARM with it, RESETS and the SERVO_WIN ring – so nothing read under the old set-up shows until core 1 has read under the new one. The ESC's telemetry and CAP_SEQ stay.

Parameters
pThe page; updated when the write is taken.
offFirst register of the window.
nNumber of registers in the window; 0 applies nothing.
inThe n register values to write.
oThe outputs model; a change while it drives is refused.
takenBit n: pin n is held by another page (supply_page_pins()).
Returns
0 when the write is taken or changes nothing; otherwise LINK_NACK_BAD_RANGE, LINK_NACK_READ_ONLY or LINK_NACK_BAD_VALUE.

◆ sense_servo_read()

void sense_servo_read ( const sense_page_t * p,
uint8_t off,
uint8_t n,
uint16_t * out )

Read SERVO_SENSE registers into out.

Parameters
pThe page.
offFirst register of the window.
nNumber of registers to read.
outReceives n register values; left unchanged for a NULL p or out, or a window past LINK_SS_COUNT.

◆ sense_servo_write()

uint8_t sense_servo_write ( sense_page_t * p,
uint8_t off,
uint8_t n,
const uint16_t * in,
const outputs_t * o )

A SERVO_SENSE write.

CAP_ARM written 0 at the head of the write disarms, is never refused, and stores nothing else of the write. Any other write is an arm, LINK_SS_CAP_FRAME registers from CAP_ARM. Refused: off the page (BAD_RANGE); a read-only register (READ_ONLY); not the whole frame, CAP_ARM with bits it does not have, an INA3221 channel that is not 1 (LINK_SS_CAP_CH) or not read, an output channel that is not a surface on a PWM slot bound to silicon (sense_page_bound()), with LINK_SS_ARM_PORT a programming port that renders no pulse (sense_page_ports()), a movement or band of 0 or past 32767mA (BAD_VALUE); an arm while o is not armed for the surfaces (NOT_ARMED). The holding level is signed mA and every value of it is taken, -32768 to

  1. An arm restarts the capture: CAP_STATE armed, the results 0.
Parameters
pThe page; updated when the write is taken.
offFirst register of the window.
nNumber of registers in the window.
inThe n register values to write.
oThe outputs model; an arm needs its surfaces armed.
Returns
0 when the write is taken; otherwise LINK_NACK_BAD_RANGE, LINK_NACK_READ_ONLY, LINK_NACK_BAD_VALUE or LINK_NACK_NOT_ARMED.

◆ sense_win_read()

void sense_win_read ( const sense_page_t * p,
uint8_t off,
uint8_t n,
uint16_t * out )

A SERVO_WIN read: CH1's ring as last published, and the capture's state, count and clipped bit as SERVO_SENSE reads them at this moment. The page takes no write.

Parameters
pThe page.
offFirst register of the window.
nNumber of registers to read.
outReceives n register values.