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

The panel's half of the SENSE (0x2B), SERVO_SENSE (0x2C) and SERVO_WIN (0x31) link pages: what is written to SENSE, in what order, what is read from the three pages and how often, and what a read means for the operator. More...

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

Go to the source code of this file.

Data Structures

struct  sense_setup_t
 The set-up as SETUP INTERFACES has it. More...
 One exchange the SENSE link asks the caller to make. More...
 One 50ms window of INA3221 CH1, as SERVO_WIN carries it. More...
 What the servo rail's meter is chosen from (servo_source.h), as of the last exchange. More...
 The panel's state of the SENSE link page. More...
 The output encoder's angle as last read. More...

Macros

#define SENSE_LINK_MINOR   7u
 The protocol minor that has SENSE and SERVO_SENSE.
#define SENSE_LINK_ENC_MINOR   9u
 The protocol minor that has SENSE's output encoder.
#define SENSE_LINK_WIN_MINOR   LINK_MINOR_SERVO_WIN
 The protocol minor that has SERVO_WIN and SENSE's RESETS.
#define SENSE_LINK_SETTLE_MS   500u
 How long a set-up rests after its last edit before it is written: a held + or - on SETUP steps a value about 30 times a second, and each write is a flash save at the IOmcu.
#define SENSE_LINK_READ_MS   40u
 SENSE's read-only registers are due this long after the last read, while a part is enabled: under the 50ms poll, so every BENCH poll reads them – 20Hz – however its exchanges move it, and a bench sample has the ESC's figures and the INA228's totals of the same poll.
#define SENSE_LINK_TOTALS_MS   100u
 The INA228's totals from a SENSE read younger than this are used over BENCH's: two polls, so one read that went unanswered does not drop the count back to BENCH's 1mAh and 0.1Wh rounding for a sample.
#define SENSE_LINK_SERVO_MS   40u
 SERVO_SENSE's windows and channel flags are due this long after the last read while the INA3221 is enabled: under the 50ms poll, so every poll reads them and every 50ms window is seen – its mean the filter, its highest current and lowest voltage the spikes.
#define SENSE_LINK_WIN_MS   40u
 SERVO_WIN's header and two newest windows are due this long after the last read while the INA3221 is enabled with CH1: under the 50ms poll, so every poll reads them.
#define SENSE_LINK_WIN_HEAD   ((unsigned)LINK_SW_ENTRY(2, 0))
 The registers of that read, and the windows they carry.
#define SENSE_LINK_WIN_HEAD_WINDOWS   2u
#define SENSE_LINK_WIN_ALL_MS   100u
 The whole page is read instead once this long has passed since a read last left no window owed: two windows, after which a third can have closed.
#define SENSE_LINK_GRACE_MS   2500u
 A part enabled and not online this long after its set-up was taken, or first read, is said not to answer: the IOmcu scans the bus again every 1000ms while a part is missing.
#define SENSE_LINK_BUS_RETRY_MS   5000u
 A bus frame refused on pins something else holds is offered again this often: freeing the pin on OUTPUTS or SUPPLY lets it through.
#define SENSE_LINK_STALE_MS   500u
 A SENSE read older than this gives no ESC figures.
#define SENSE_LINK_STATUS_COUNT   ((unsigned)LINK_SN_COUNT - (unsigned)LINK_SN_FLAGS)
 The registers read from SENSE: FLAGS to the last, AS5600_STILL_MS's neighbour; and without the encoder FLAGS to ESC_FLAGS.
#define SENSE_LINK_STATUS_COUNT_V48   ((unsigned)LINK_SN_COUNT_V48 - (unsigned)LINK_SN_FLAGS)
#define SENSE_LINK_SERVO_COUNT   ((unsigned)LINK_SS_CH_FLAGS + 1u)
 And from SERVO_SENSE: the three channels' windows, WINDOW and CH_FLAGS.
#define SENSE_LINK_NO_ANSWER   (-1)
 What came of an exchange: ACK or DATA, a NACK's reason, or none.
#define SENSE_LINK_ACK   0
#define SENSE_LINK_EV_SETUP
 The events about the set-up asked, which sense_link_event_stands() judges: what ends them is an edit on SETUP and a write the page takes.
#define SENSE_LINK_WINDOW_MS   50u
 One window of the IOmcu's, in ms: SENSE_WINDOW_MS.
#define SENSE_LINK_EVENT_GAP_MS   5000u
 How long an event handed out by sense_link_event() has the alert band before the next one is handed out.

Enumerations

enum  sense_link_part_t { SENSE_LINK_INA228 = 0 , SENSE_LINK_INA3221 = 1 }
 The two parts, for the accessors.
enum  {
  SENSE_LINK_EV_NO_PAGE = 0x0001 , SENSE_LINK_EV_PINS_UNSET = 0x0002 , SENSE_LINK_EV_SAME_ADDR = 0x0004 , SENSE_LINK_EV_BUS_REFUSED = 0x0008 ,
  SENSE_LINK_EV_I228_REFUSED = 0x0010 , SENSE_LINK_EV_I3221_REFUSED = 0x0020 , SENSE_LINK_EV_I228_SILENT = 0x0040 , SENSE_LINK_EV_I3221_SILENT = 0x0080 ,
  SENSE_LINK_EV_I228_WRONG = 0x0100 , SENSE_LINK_EV_I3221_WRONG = 0x0200 , SENSE_LINK_EV_STUCK = 0x0400 , SENSE_LINK_EV_I228_CLIPPED = 0x0800 ,
  SENSE_LINK_EV_I3221_CLIPPED = 0x1000 , SENSE_LINK_EV_STORE_OFF = 0x2000 , SENSE_LINK_EV_ENC_OLD = 0x4000 , SENSE_LINK_EV_ENC_SILENT = 0x8000 ,
  SENSE_LINK_EV_ENC_MAGNET = 0x10000
}
 Things the caller tells the operator about, each once. More...
enum  sense_link_op_kind_t {
  SENSE_LINK_OP_NONE = 0 , SENSE_LINK_OP_READ_SETUP , SENSE_LINK_OP_OFF , SENSE_LINK_OP_I228 ,
  SENSE_LINK_OP_I3221 , SENSE_LINK_OP_BUS , SENSE_LINK_OP_IDENTITY , SENSE_LINK_OP_STATUS ,
  SENSE_LINK_OP_SERVO , SENSE_LINK_OP_WIN , SENSE_LINK_OP_WIN_ALL
}
 The exchange sense_link_next() asks for. More...
enum  {
  SENSE_LINK_ROW_I228 = 0x0001 , SENSE_LINK_ROW_I3221 = 0x0002 , SENSE_LINK_ROW_ENC = 0x0004 , SENSE_LINK_ROW_PINS = 0x0008 ,
  SENSE_LINK_ROW_I228_ADDR = 0x0010 , SENSE_LINK_ROW_I228_SHUNT = 0x0020 , SENSE_LINK_ROW_I228_MAX = 0x0040 , SENSE_LINK_ROW_I3221_ADDR = 0x0080 ,
  SENSE_LINK_ROW_I3221_SHUNT = 0x0100 , SENSE_LINK_ROW_I3221_CH = 0x0200
}
 The rows of SETUP INTERFACES, for sense_link_unheld(). More...

Functions

void sense_link_init (sense_link_t *s)
 Sets up the SENSE link state.
void sense_link_lost (sense_link_t *s)
 The link went: nothing on the page is known, and nothing is read or written until it comes up again.
void sense_link_came_up (sense_link_t *s, uint16_t minor, uint32_t now_ms)
 An IOmcu answered with protocol minor minor.
void sense_link_want (sense_link_t *s, const sense_setup_t *w, uint32_t now_ms)
 The set-up SETUP names now.
bool sense_link_next (sense_link_t *s, uint32_t now_ms, bool idle, sense_link_op_t *op)
 The next exchange owed at now_ms, into op; false for none.
void sense_link_done (sense_link_t *s, int result, const uint16_t *regs, uint32_t now_ms)
 How the exchange sense_link_next() gave went: SENSE_LINK_ACK with the registers read (regs, op.n of them; NULL for a write), a NACK reason, or SENSE_LINK_NO_ANSWER.
void sense_link_faults (sense_link_t *s, uint16_t faults)
 STATUS's faults word, as the caller last read it.
uint32_t sense_link_events (sense_link_t *s)
 The events since the last call, SENSE_LINK_EV_*, and cleared.
uint32_t sense_link_event (sense_link_t *s, uint32_t now_ms)
 One event for the alert band, which shows one line and replaces it with the next: the most pressing waiting – no page, a set-up not sent or refused, a stuck bus, a part not answering, another identity, a clipped reading, the store off – cleared, and the rest kept.
bool sense_link_event_stands (const sense_link_t *s, uint32_t ev)
 Whether what ev, one SENSE_LINK_EV_* bit, says still holds now.
void sense_link_event_back (sense_link_t *s, uint32_t ev)
 Put back an event sense_link_event() handed out that never reached the band – another alert replaced it first – with what it was raised with, so it is handed out again after the gap.
bool sense_link_settled (const sense_link_t *s)
 Whether nothing is owed: the page holds the set-up asked, less the frames it refused.
uint16_t sense_link_unheld (const sense_link_t *s)
 The rows whose value as asked is not the one the page holds, SENSE_LINK_ROW_*: a set-up not written yet – it rests SENSE_LINK_SETTLE_MS and waits for an idle bank – one the page refused, and a part that is on in the settings and off on the page because its frame was refused, its pins are unset, both monitors share an address or the IOmcu is older than the part.
void sense_link_set_map (sense_link_t *s, bool map)
 Whether the INA3221 parts go by SENSE_MAP (sense_map_link_page()): then ENABLE bit 1 and the INA3221's frame are the IOmcu's mirror of the map, kept as read and never written, and refuse nothing.
void sense_link_set_map_held (sense_link_t *s, bool held)
 Whether SENSE_MAP holds the map SETUP asks (sense_map_link_held()): with the map in force the meter is held only then.
void sense_link_reread (sense_link_t *s)
 Read SENSE's set-up again, and the identity after it: a SENSE_MAP write was taken, and the mirror registers moved with it.
bool sense_link_take_caps (sense_link_t *s, uint16_t *caps)
 The capability bits an identity read since the last call brought, into caps; false if none did.
uint16_t sense_link_flags (const sense_link_t *s)
 FLAGS as last read, 0 before any read.
bool sense_link_win_on (const sense_link_t *s)
 Whether the INA3221's windows come from SERVO_WIN: the IOmcu has the page and its SENSE page enables the INA3221 with CH1.
bool sense_link_take_win (sense_link_t *s, sense_link_win_t *out)
 The next CH1 window of the ring into out, oldest first, each number once: false when none waits, out untouched.
uint32_t sense_link_win_lost (const sense_link_t *s)
 Window numbers that left the ring before a read took them, since init, modulo 2^32: the windows lost, and with them any number the IOmcu skipped that was out of the ring by the read.
unsigned sense_link_windows (const sense_link_t *s)
 How many windows sense_link_take_window() hands over now: the ring's waiting ones, or without the ring 1 while SERVO_SENSE's last read holds a window not taken yet.
void sense_link_win_bench (const sense_link_t *s, const sense_link_win_t *w, bench_state_t *b)
 A ring window into b's log fields: servo_new set, its number, CH1's mean and highest current and lowest bus voltage with servo_ok bit 0 set while the window holds samples, servo_no_current and servo_no_voltage bit 0 for the quantity it holds no sample of, and CH2's and CH3's figures from SERVO_SENSE's last read when that read shows the same window number.
bool sense_link_take_window (sense_link_t *s, bench_state_t *b)
 The INA3221's next window into b for the log, once per window; true and servo_new set, false and b untouched otherwise.
void sense_link_meter (const sense_link_t *s, sense_link_meter_t *out)
 What the servo rail's meter is chosen from, into out.
uint32_t sense_link_reads (const sense_link_t *s)
 SENSE reads answered since init: a caller that compares two counts can tell a read made in between.
bool sense_link_esc (const sense_link_t *s, uint32_t now_ms, bool *v_ok, float *volts, bool *i_ok, float *amps)
 The ESC's own telemetry voltage and current, from the last SENSE read if it is younger than SENSE_LINK_STALE_MS, each with its valid bit.
bool sense_link_totals (const sense_link_t *s, uint32_t now_ms, int32_t *charge_cmah, uint32_t *energy_cwh)
 The INA228's charge in 0.01mAh and energy in 0.01Wh as last read, 0 while they are not the run's totals.
bool sense_link_enc (const sense_link_t *s, uint32_t now_ms, sense_link_enc_t *out)
 The encoder's last angle into out: true when the page enables it, the last SENSE read is younger than SENSE_LINK_STALE_MS at now_ms, the part was online in it, the angle holds a reading of this set-up and that reading's STATUS has MD set: a magnet is detected, and RAW ANGLE is a position.
bool sense_link_enc_no_magnet (const sense_link_t *s, uint32_t now_ms)
 Whether sense_link_enc() gives no angle at now_ms because the part answers and sees no magnet: every condition of sense_link_enc() but MD holds.
uint16_t sense_link_enc_magnet (const sense_link_t *s)
 The magnet bits SENSE_LINK_EV_ENC_MAGNET was raised with: LINK_SN_ENC_MD, _ML and _MH, as read.
bool sense_link_enc_on (const sense_link_t *s)
 Whether the encoder is set up on the page: enabled there, and read.
uint8_t sense_link_addr (const sense_link_t *s, sense_link_part_t part)
 The address part is set up at on the page, 0 while not known.
uint16_t sense_link_id (const sense_link_t *s, sense_link_part_t part)
 The ID part gave, as last read (DEVICE_ID, the die ID).
uint8_t sense_link_found (const sense_link_t *s, sense_link_part_t part)
 Another address that answered the last scan where part could be: an INA228 at 0x40 to 0x4F, an INA3221 at 0x40 to 0x43, neither at the other part's address while that one is enabled.
uint8_t sense_link_clipped_channel (const sense_link_t *s)
 The INA3221 channel SENSE_LINK_EV_I3221_CLIPPED names, 1 to 3: the one sense_link_event() last handed out.
uint16_t sense_link_event_id (const sense_link_t *s, sense_link_part_t part)
 What an event was raised with, kept for when the band shows it: the ID part gave for SENSE_LINK_EV_*_WRONG, and the address the scan found for SENSE_LINK_EV_*_SILENT (0 none).
uint8_t sense_link_event_found (const sense_link_t *s, sense_link_part_t part)
 The address a scan found for the event's part, or 0 for none.
uint16_t sense_link_i3221_dmohm (const sense_link_t *s)
 The INA3221's shunt on the page, 0.1mOhm.
uint8_t sense_link_i3221_addr (const sense_link_t *s)
 The INA3221's address on the page, the part SERVO_SENSE reads: 0x40 to 0x43; 0 before the page is read.
int sense_link_sda (const sense_link_t *s)
 The pins asked, -1 for unset: what SENSE_LINK_EV_BUS_REFUSED names.
int sense_link_scl (const sense_link_t *s)
 The SCL pin asked, -1 for unset.
bool sense_link_held_pins (const sense_link_t *s, int *sda, int *scl)
 The sensor bus's pins the page holds by its last read or write, as the IOmcu reserves them (sense_page_pins()): SDA and SCL while ENABLE has a part on, -1 and -1 while it has none or the IOmcu has no SENSE page.

Detailed Description

The panel's half of the SENSE (0x2B), SERVO_SENSE (0x2C) and SERVO_WIN (0x31) link pages: what is written to SENSE, in what order, what is read from the three pages and how often, and what a read means for the operator.

The IOmcu reads the two current monitors and the output encoder (an AS5600 angle sensor); the panel sets them up from SETUP INTERFACES and reads what they measured. An IOmcu whose protocol minor is below SENSE_LINK_MINOR has neither page, and nothing is sent to it: not a write and not a read. One below SENSE_LINK_ENC_MINOR has no encoder: ENABLE bit 2 is not written, SENSE 26 to 31 are not read, and an encoder enabled on SETUP raises SENSE_LINK_EV_ENC_OLD. One below SENSE_LINK_WIN_MINOR has no SERVO_WIN and no RESETS: nothing is sent to SERVO_WIN, and the INA3221's windows are SERVO_SENSE's last one per read.

The set-up is three frames of four registers: the bus (ENABLE to KHZ), the INA228 (4 to 7) and the INA3221 (8 to 11). The page judges every frame against the whole set-up it would leave, so the order is fixed:

  1. what the page holds is read first, SENSE 0 to 11, once per link-up, and nothing is written that it already holds;
  2. while a part's frame is to change and the page enables both parts, the bus frame with ENABLE 0 and the page's own pins, so no intermediate set-up has two enabled parts on one address;
  3. the part frames that differ;
  4. the bus frame: the pins, and ENABLE without a part whose frame was refused, so no part runs on a shunt or an address it was not given.

Writes go out only while the caller says the bank is idle – the page refuses any change while it drives – and only once the set-up has rested SENSE_LINK_SETTLE_MS since its last edit, because the IOmcu keeps each change in flash. A frame the page refuses is not written again until the set-up changes – except a bus frame whose SDA and SCL are one I2C block's pair, refused for pins something else holds, which is offered again every SENSE_LINK_BUS_RETRY_MS and said once. Two set-ups are not written at all, and said once per edit: a part enabled with SDA or SCL unset, and both parts enabled on one address; neither part is enabled on the page then.

After a write is taken, the identity page is read again: its capability bits 3 and 4 say what the SENSE set-up enables, and change only with a SENSE write.

While the page enables a part, SENSE 12 to 25 – to 31 with the encoder – are read every SENSE_LINK_READ_MS and, while it enables the INA3221, SERVO_SENSE 0 to 13 every SENSE_LINK_SERVO_MS. From those reads come the events the caller tells the operator about, each once: a part enabled and not answering, a part that answers with another identity, the bus stuck, and a reading clipped at the top of its range.

The window ring. While the page of a 4.11 IOmcu enables the INA3221 with CH1, SERVO_WIN's registers 0 to 15 – the header and the two newest windows – are read every SENSE_LINK_WIN_MS, and SENSE's status read goes to register 31 for RESETS, as it does with the encoder. Each CH1 window number is handed over once, oldest first (sense_link_take_win()):

  • a read that owes one or two windows hands them over;
  • SENSE_LINK_WIN_ALL_MS after the last read that left nothing owed, the read is the whole page, 28 registers, so every window owed comes from one reply. Entries of two replies are not joined: a window boundary between them shifts every entry by one number;
  • a read of registers 0 to 15 that owes more than its two windows hands nothing over and asks for the whole page in the same poll;
  • a number more than LINK_SW_RING behind the newest has left the ring and is counted (sense_link_win_lost());
  • an entry whose CLOSED bit is clear is a number the IOmcu skipped: nothing is handed over for it and nothing is counted. A skipped number that left the ring before a read is counted as a window is: the page no longer says which it was;
  • a reply lost on the link moves nothing: the next read owes the same windows and those closed since;
  • the first read after a link-up hands nothing over and starts after the newest number it shows, so a window read before the link went is not handed over again;
  • a set-up this module wrote, a read without LINK_SW_HAVE, and a newest number behind the last one handed over mean the ring started again: every closed entry of the next read is owed;
  • at most LINK_SW_RING windows wait to be taken; a read that brings more leaves the rest owed on the page.

Pure C, no link of its own: the caller makes each exchange and reports how it went. Control task only.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ SENSE_LINK_EV_SETUP

#define SENSE_LINK_EV_SETUP
Value:

The events about the set-up asked, which sense_link_event_stands() judges: what ends them is an edit on SETUP and a write the page takes.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

Things the caller tells the operator about, each once.

Enumerator
SENSE_LINK_EV_NO_PAGE 

A part is enabled and the IOmcu has no SENSE page.

SENSE_LINK_EV_PINS_UNSET 

A part is enabled with SDA or SCL unset: none is enabled.

SENSE_LINK_EV_SAME_ADDR 

Both parts enabled on one address: neither is.

SENSE_LINK_EV_BUS_REFUSED 

The page refused the bus frame: the pins, as a rule.

SENSE_LINK_EV_I228_REFUSED 

The page refused the INA228's frame: its shunt and range.

SENSE_LINK_EV_I3221_REFUSED 

The page refused the INA3221's frame.

SENSE_LINK_EV_I228_SILENT 

The INA228 is enabled and does not answer at its address.

SENSE_LINK_EV_I228_WRONG 

Something answers at the INA228's address with another identity.

SENSE_LINK_EV_STUCK 

SDA held low; the IOmcu clocks it free.

SENSE_LINK_EV_I228_CLIPPED 

The INA228's current read at the end of its range.

SENSE_LINK_EV_I3221_CLIPPED 

An INA3221 channel read 163.8mV across its shunt; see sense_link_clipped_channel().

SENSE_LINK_EV_STORE_OFF 

STATUS fault bit 6: the IOmcu saves nothing this boot.

SENSE_LINK_EV_ENC_OLD 

The encoder is enabled and the IOmcu is older than 4.9.

SENSE_LINK_EV_ENC_SILENT 

The encoder is enabled and does not answer at 0x36, or something answers there that is not an AS5600.

SENSE_LINK_EV_ENC_MAGNET 

The encoder answers and its magnet is missing, too weak or too strong; see sense_link_enc_magnet().

Said once while the magnet bits (MD, ML, MH) stay as they were said, across the set-ups an edit on SETUP writes; said again when a bit changes – a weak magnet that goes missing, a missing one that comes back weak, a weak one that reads strong – when the field read right in between, after the encoder was said not to answer, after a set-up that switches it off, and after a link-up.

◆ anonymous enum

anonymous enum

The rows of SETUP INTERFACES, for sense_link_unheld().

Enumerator
SENSE_LINK_ROW_I228 

INA228 on or off

SENSE_LINK_ROW_I3221 

INA3221 on or off

SENSE_LINK_ROW_ENC 

AS5600 on or off

SENSE_LINK_ROW_PINS 

Sensor SDA and SCL

◆ sense_link_op_kind_t

The exchange sense_link_next() asks for.

Enumerator
SENSE_LINK_OP_READ_SETUP 

SENSE 0 to 11: what the page holds

SENSE_LINK_OP_OFF 

SENSE 0 to 3, both parts off

SENSE_LINK_OP_I228 

SENSE 4 to 7

SENSE_LINK_OP_I3221 

SENSE 8 to 11

SENSE_LINK_OP_BUS 

SENSE 0 to 3

SENSE_LINK_OP_IDENTITY 

IDENTITY, for the capability bits

SENSE_LINK_OP_STATUS 

SENSE 12 to 25, or to 31

SENSE_LINK_OP_SERVO 

SERVO_SENSE 0 to 13

SENSE_LINK_OP_WIN 

SERVO_WIN 0 to 15

SENSE_LINK_OP_WIN_ALL 

SERVO_WIN 0 to 27

Function Documentation

◆ sense_link_addr()

uint8_t sense_link_addr ( const sense_link_t * s,
sense_link_part_t part )

The address part is set up at on the page, 0 while not known.

Parameters
sThe link state.
partThe part.
Returns
The address, or 0 while not known.

◆ sense_link_came_up()

void sense_link_came_up ( sense_link_t * s,
uint16_t minor,
uint32_t now_ms )

An IOmcu answered with protocol minor minor.

Below SENSE_LINK_MINOR it has neither page and nothing is sent to it, and a part enabled on SETUP raises SENSE_LINK_EV_NO_PAGE.

Parameters
sThe link state.
minorThe IOmcu's protocol minor version.
now_msThe time, in ms.

◆ sense_link_clipped_channel()

uint8_t sense_link_clipped_channel ( const sense_link_t * s)

The INA3221 channel SENSE_LINK_EV_I3221_CLIPPED names, 1 to 3: the one sense_link_event() last handed out.

Channels that clip together are handed out one each, the event standing until the last.

Parameters
sThe link state.
Returns
The INA3221 channel, 1 to 3.

◆ sense_link_done()

void sense_link_done ( sense_link_t * s,
int result,
const uint16_t * regs,
uint32_t now_ms )

How the exchange sense_link_next() gave went: SENSE_LINK_ACK with the registers read (regs, op.n of them; NULL for a write), a NACK reason, or SENSE_LINK_NO_ANSWER.

Parameters
sThe link state.
resultThe result: an acknowledgement, a NACK's reason or SENSE_LINK_NO_ANSWER.
regsThe registers read, or NULL for a write.
now_msThe time, in ms.

◆ sense_link_enc()

bool sense_link_enc ( const sense_link_t * s,
uint32_t now_ms,
sense_link_enc_t * out )

The encoder's last angle into out: true when the page enables it, the last SENSE read is younger than SENSE_LINK_STALE_MS at now_ms, the part was online in it, the angle holds a reading of this set-up and that reading's STATUS has MD set: a magnet is detected, and RAW ANGLE is a position.

False, out untouched, otherwise. This is the one place an angle leaves the link, so no caller has an angle read without a magnet.

Parameters
sThe link state.
now_msThe time, in ms.
outReceives the angle.
Returns
true when an angle is given.

◆ sense_link_enc_magnet()

uint16_t sense_link_enc_magnet ( const sense_link_t * s)

The magnet bits SENSE_LINK_EV_ENC_MAGNET was raised with: LINK_SN_ENC_MD, _ML and _MH, as read.

Parameters
sThe link state.
Returns
The magnet bits, as read.

◆ sense_link_enc_no_magnet()

bool sense_link_enc_no_magnet ( const sense_link_t * s,
uint32_t now_ms )

Whether sense_link_enc() gives no angle at now_ms because the part answers and sees no magnet: every condition of sense_link_enc() but MD holds.

Parameters
sThe link state.
now_msThe time, in ms.
Returns
true when the encoder answers and sees no magnet.

◆ sense_link_enc_on()

bool sense_link_enc_on ( const sense_link_t * s)

Whether the encoder is set up on the page: enabled there, and read.

Parameters
sThe link state.
Returns
true when the encoder is set up on the page.

◆ sense_link_esc()

bool sense_link_esc ( const sense_link_t * s,
uint32_t now_ms,
bool * v_ok,
float * volts,
bool * i_ok,
float * amps )

The ESC's own telemetry voltage and current, from the last SENSE read if it is younger than SENSE_LINK_STALE_MS, each with its valid bit.

False, both bits clear, otherwise.

Parameters
sThe link state.
now_msThe time, in ms.
v_okReceives the valid bit of the voltage.
voltsReceives the voltage, in V.
i_okReceives the valid bit of the current.
ampsReceives the current, in A.
Returns
true when the telemetry is available.

◆ sense_link_event()

uint32_t sense_link_event ( sense_link_t * s,
uint32_t now_ms )

One event for the alert band, which shows one line and replaces it with the next: the most pressing waiting – no page, a set-up not sent or refused, a stuck bus, a part not answering, another identity, a clipped reading, the store off – cleared, and the rest kept.

0 while none waits or while the last one handed out is younger than SENSE_LINK_EVENT_GAP_MS at now_ms.

Parameters
sThe link state.
now_msThe time, in ms.
Returns
The event bit for the alert band, or 0 for none.

◆ sense_link_event_back()

void sense_link_event_back ( sense_link_t * s,
uint32_t ev )

Put back an event sense_link_event() handed out that never reached the band – another alert replaced it first – with what it was raised with, so it is handed out again after the gap.

A clipped event takes its channel back. An event about an IOmcu is not put back once the link has gone.

Parameters
sThe link state.
evThe event to put back.

◆ sense_link_event_found()

uint8_t sense_link_event_found ( const sense_link_t * s,
sense_link_part_t part )

The address a scan found for the event's part, or 0 for none.

Parameters
sThe link state.
partThe part.
Returns
The address a scan found for the event.

◆ sense_link_event_id()

uint16_t sense_link_event_id ( const sense_link_t * s,
sense_link_part_t part )

What an event was raised with, kept for when the band shows it: the ID part gave for SENSE_LINK_EV_*_WRONG, and the address the scan found for SENSE_LINK_EV_*_SILENT (0 none).

A later read can show another; a part that answers as itself before the band shows either drops it.

Parameters
sThe link state.
partThe part.
Returns
The ID the part gave for a WRONG event.

◆ sense_link_event_stands()

bool sense_link_event_stands ( const sense_link_t * s,
uint32_t ev )

Whether what ev, one SENSE_LINK_EV_* bit, says still holds now.

For the events about the set-up asked: SENSE_LINK_EV_PINS_UNSET and SENSE_LINK_EV_SAME_ADDR while the settings ask it; a refusal – SENSE_LINK_EV_BUS_REFUSED, _I228_REFUSED, _I3221_REFUSED – until the page is known to hold the frame as asked: not refused, and written and taken. A refused value stepped back on SETUP ends it once the IOmcu takes the frame; the rest and the write take SENSE_LINK_SETTLE_MS and a poll. With no IOmcu answering a refusal stands. True for every other event: what a read raised ends with the band's own time.

The caller takes back an alert it showed for such an event once this is false; sense_link_event() drops one waiting that is over.

Parameters
sThe link state.
evOne SENSE_LINK_EV_* bit.
Returns
true while the cause of the event holds.

◆ sense_link_events()

uint32_t sense_link_events ( sense_link_t * s)

The events since the last call, SENSE_LINK_EV_*, and cleared.

Parameters
sThe link state.
Returns
The events as SENSE_LINK_EV_* bits.

◆ sense_link_faults()

void sense_link_faults ( sense_link_t * s,
uint16_t faults )

STATUS's faults word, as the caller last read it.

Parameters
sThe link state.
faultsThe STATUS faults word.

◆ sense_link_flags()

uint16_t sense_link_flags ( const sense_link_t * s)

FLAGS as last read, 0 before any read.

Parameters
sThe link state.
Returns
FLAGS as last read.

◆ sense_link_found()

uint8_t sense_link_found ( const sense_link_t * s,
sense_link_part_t part )

Another address that answered the last scan where part could be: an INA228 at 0x40 to 0x4F, an INA3221 at 0x40 to 0x43, neither at the other part's address while that one is enabled.

The lowest; 0 for none.

Parameters
sThe link state.
partThe part.
Returns
The lowest other address that answered, or 0 for none.

◆ sense_link_held_pins()

bool sense_link_held_pins ( const sense_link_t * s,
int * sda,
int * scl )

The sensor bus's pins the page holds by its last read or write, as the IOmcu reserves them (sense_page_pins()): SDA and SCL while ENABLE has a part on, -1 and -1 while it has none or the IOmcu has no SENSE page.

False, both -1, while that is not known: no link, or no read yet. sda and scl may be NULL.

Parameters
sThe link state.
sdaReceives the SDA pin, or -1; may be NULL.
sclReceives the SCL pin, or -1; may be NULL.
Returns
true when the held pins are known.

◆ sense_link_i3221_addr()

uint8_t sense_link_i3221_addr ( const sense_link_t * s)

The INA3221's address on the page, the part SERVO_SENSE reads: 0x40 to 0x43; 0 before the page is read.

Parameters
sThe link state.
Returns
The INA3221's address, 0x40 to 0x43, or 0 before the page is read.

◆ sense_link_i3221_dmohm()

uint16_t sense_link_i3221_dmohm ( const sense_link_t * s)

The INA3221's shunt on the page, 0.1mOhm.

Parameters
sThe link state.
Returns
The INA3221's shunt on the page, in 0.1mOhm.

◆ sense_link_id()

uint16_t sense_link_id ( const sense_link_t * s,
sense_link_part_t part )

The ID part gave, as last read (DEVICE_ID, the die ID).

Parameters
sThe link state.
partThe part.
Returns
The ID the part gave.

◆ sense_link_init()

void sense_link_init ( sense_link_t * s)

Sets up the SENSE link state.

Parameters
sThe link state to set up.

◆ sense_link_lost()

void sense_link_lost ( sense_link_t * s)

The link went: nothing on the page is known, and nothing is read or written until it comes up again.

Parameters
sThe link state.

◆ sense_link_meter()

void sense_link_meter ( const sense_link_t * s,
sense_link_meter_t * out )

What the servo rail's meter is chosen from, into out.

Parameters
sThe link state.
outReceives the meter's choice.

◆ sense_link_next()

bool sense_link_next ( sense_link_t * s,
uint32_t now_ms,
bool idle,
sense_link_op_t * op )

The next exchange owed at now_ms, into op; false for none.

Writes only while idle: the bank is disarmed here and at the far end. The caller makes the exchange and reports with sense_link_done().

Parameters
sThe link state.
now_msThe time, in ms.
idletrue while the bank is disarmed here and at the far end.
opReceives the exchange owed.
Returns
true when an exchange is owed.

◆ sense_link_reads()

uint32_t sense_link_reads ( const sense_link_t * s)

SENSE reads answered since init: a caller that compares two counts can tell a read made in between.

Parameters
sThe link state.
Returns
The SENSE reads answered since init.

◆ sense_link_reread()

void sense_link_reread ( sense_link_t * s)

Read SENSE's set-up again, and the identity after it: a SENSE_MAP write was taken, and the mirror registers moved with it.

Parameters
sThe link state.

◆ sense_link_scl()

int sense_link_scl ( const sense_link_t * s)

The SCL pin asked, -1 for unset.

Parameters
sThe link state.
Returns
The SCL pin asked, or -1 for unset.

◆ sense_link_sda()

int sense_link_sda ( const sense_link_t * s)

The pins asked, -1 for unset: what SENSE_LINK_EV_BUS_REFUSED names.

Parameters
sThe link state.
Returns
The SDA pin asked, or -1 for unset.

◆ sense_link_set_map()

void sense_link_set_map ( sense_link_t * s,
bool map )

Whether the INA3221 parts go by SENSE_MAP (sense_map_link_page()): then ENABLE bit 1 and the INA3221's frame are the IOmcu's mirror of the map, kept as read and never written, and refuse nothing.

Parameters
sThe link state.
maptrue when the INA3221 parts go by SENSE_MAP.

◆ sense_link_set_map_held()

void sense_link_set_map_held ( sense_link_t * s,
bool held )

Whether SENSE_MAP holds the map SETUP asks (sense_map_link_held()): with the map in force the meter is held only then.

Parameters
sThe link state.
heldtrue while SENSE_MAP holds the map SETUP asks.

◆ sense_link_settled()

bool sense_link_settled ( const sense_link_t * s)

Whether nothing is owed: the page holds the set-up asked, less the frames it refused.

Parameters
sThe link state.
Returns
true when nothing is owed.

◆ sense_link_take_caps()

bool sense_link_take_caps ( sense_link_t * s,
uint16_t * caps )

The capability bits an identity read since the last call brought, into caps; false if none did.

Parameters
sThe link state.
capsReceives the capability bits.
Returns
true when an identity read since the last call brought bits.

◆ sense_link_take_win()

bool sense_link_take_win ( sense_link_t * s,
sense_link_win_t * out )

The next CH1 window of the ring into out, oldest first, each number once: false when none waits, out untouched.

Parameters
sThe link state.
outReceives the window.
Returns
true when a window was taken.

◆ sense_link_take_window()

bool sense_link_take_window ( sense_link_t * s,
bench_state_t * b )

The INA3221's next window into b for the log, once per window; true and servo_new set, false and b untouched otherwise.

With the ring (sense_link_win_on()): the next CH1 window, as sense_link_take_win() and sense_link_win_bench() give it. Call until false: a poll brings up to LINK_SW_RING windows.

Without it: SERVO_SENSE's last read, when it holds a window with readings whose number has not been taken before.

Parameters
sThe link state.
bReceives the window for the log.
Returns
true when a window was taken.

◆ sense_link_totals()

bool sense_link_totals ( const sense_link_t * s,
uint32_t now_ms,
int32_t * charge_cmah,
uint32_t * energy_cwh )

The INA228's charge in 0.01mAh and energy in 0.01Wh as last read, 0 while they are not the run's totals.

False before any read and for a read older than SENSE_LINK_TOTALS_MS at now_ms.

Parameters
sThe link state.
now_msThe time, in ms.
charge_cmahReceives the charge, in 0.01mAh.
energy_cwhReceives the energy, in 0.01Wh.
Returns
true when the totals are valid.

◆ sense_link_unheld()

uint16_t sense_link_unheld ( const sense_link_t * s)

The rows whose value as asked is not the one the page holds, SENSE_LINK_ROW_*: a set-up not written yet – it rests SENSE_LINK_SETTLE_MS and waits for an idle bank – one the page refused, and a part that is on in the settings and off on the page because its frame was refused, its pins are unset, both monitors share an address or the IOmcu is older than the part.

The comparison is want against held, the two this module writes from. 0 while no IOmcu answers and before the page has been read: nothing is known to compare with. An IOmcu without the page holds no part, and the parts that are on are named.

Parameters
sThe link state.
Returns
The SENSE_LINK_ROW_* bits of the rows not held as asked.

◆ sense_link_want()

void sense_link_want ( sense_link_t * s,
const sense_setup_t * w,
uint32_t now_ms )

The set-up SETUP names now.

A part enabled while an IOmcu without the page answers raises SENSE_LINK_EV_NO_PAGE. A set-up with a value outside the page's range – a zeroed snapshot – is ignored: nothing is asked, and nothing is written for it.

Parameters
sThe link state.
wThe set-up SETUP names.
now_msThe time, in ms.

◆ sense_link_win_bench()

void sense_link_win_bench ( const sense_link_t * s,
const sense_link_win_t * w,
bench_state_t * b )

A ring window into b's log fields: servo_new set, its number, CH1's mean and highest current and lowest bus voltage with servo_ok bit 0 set while the window holds samples, servo_no_current and servo_no_voltage bit 0 for the quantity it holds no sample of, and CH2's and CH3's figures from SERVO_SENSE's last read when that read shows the same window number.

Parameters
sThe link state.
wThe ring window.
bReceives the log fields.

◆ sense_link_win_lost()

uint32_t sense_link_win_lost ( const sense_link_t * s)

Window numbers that left the ring before a read took them, since init, modulo 2^32: the windows lost, and with them any number the IOmcu skipped that was out of the ring by the read.

A caller that compares two counts has those of the time in between.

Parameters
sThe link state.
Returns
The windows lost since init, modulo 2^32.

◆ sense_link_win_on()

bool sense_link_win_on ( const sense_link_t * s)

Whether the INA3221's windows come from SERVO_WIN: the IOmcu has the page and its SENSE page enables the INA3221 with CH1.

Parameters
sThe link state.
Returns
true when the INA3221's windows come from SERVO_WIN.

◆ sense_link_windows()

unsigned sense_link_windows ( const sense_link_t * s)

How many windows sense_link_take_window() hands over now: the ring's waiting ones, or without the ring 1 while SERVO_SENSE's last read holds a window not taken yet.

Parameters
sThe link state.
Returns
The number of windows sense_link_take_window() hands over now.