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

The sensor bus: one I2C (Inter-Integrated Circuit) bus with current monitors on it, as the drivers see it. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

Go to the source code of this file.

Data Structures

struct  sense_i2c_t
 The wire. n is the register width in bytes: 2, 3 or 5 for a read (INA228 SLYS021A Table 7-3, INA3221 SBOS576C Table 8-1), 2 for a write. More...
struct  sense_bus_t
struct  sense_part_t
 A part on the bus: its address, state and last probe. More...
struct  sense_value_t
 A measurement, or a clip. More...

Macros

#define SENSE_STUCK_TIMEOUTS   2u
#define SENSE_RECOVER_MS   100u
#define SENSE_FAILS   3u
#define SENSE_RETRY_MS   1000u

Enumerations

enum  sense_err_t {
  SENSE_OK = 0 , SENSE_NACK , SENSE_TIMEOUT , SENSE_BUS_LOW ,
  SENSE_STUCK , SENSE_OFFLINE , SENSE_BAD_ARG
}
 What a transaction came to. More...
enum  sense_state_t {
  SENSE_PART_UNSET = 0 , SENSE_PART_UNPROBED , SENSE_PART_ABSENT , SENSE_PART_WRONG_ID ,
  SENSE_PART_REFUSED , SENSE_PART_ONLINE , SENSE_PART_OFFLINE
}
 A part on the bus, as far as it is known. More...
enum  sense_clip_t { SENSE_CLIP_LOW = -1 , SENSE_CLIP_NONE = 0 , SENSE_CLIP_HIGH = 1 }
 A reading that may be at the end of its register's range. More...

Functions

void sense_bus_init (sense_bus_t *b, const sense_i2c_t *io)
 Set up a bus over a wire.
sense_err_t sense_bus_read (sense_bus_t *b, uint8_t addr, uint8_t reg, uint8_t *buf, size_t n)
 A register read: n bytes, most significant first, into buf.
sense_err_t sense_bus_write16 (sense_bus_t *b, uint8_t addr, uint8_t reg, uint16_t value)
 A 16-bit register write.
void sense_bus_note (sense_bus_t *b, sense_err_t e)
 A transaction made on the wire without this module – an address scan – and what it came to, for the stuck decision: a held line or a timeout counts as one of this module's would, and marks the bus stuck by the same rule.
bool sense_bus_recovery_due (const sense_bus_t *b, uint32_t now_ms)
 Whether a recovery is to be run now: the bus is stuck, and none has run since, or the last ran SENSE_RECOVER_MS or more ago without a transaction proving the bus free.
void sense_bus_recovered (sense_bus_t *b, uint32_t now_ms)
 The caller has run a recovery.
void sense_part_init (sense_part_t *p, sense_bus_t *bus, uint8_t addr)
 Set up a part record on a bus.
bool sense_part_probe_due (sense_part_t *p, uint32_t now_ms)
 Every tick, with the time: whether the part is to be probed now.
void sense_part_probed (sense_part_t *p, sense_state_t state)
 The probe's result; a part left anything but online is probed again SENSE_RETRY_MS on.
sense_err_t sense_part_read (sense_part_t *p, uint8_t reg, uint8_t *buf, size_t n)
 A read or write of an online part, counted towards taking it offline.
sense_err_t sense_part_write16 (sense_part_t *p, uint8_t reg, uint16_t value)
 Write a 16-bit register of a part, counted towards taking it offline.
void sense_part_sample_failed (sense_part_t *p, uint8_t before)
 After a sample of two reads whose second failed: the failure run is before plus one, as if the first read had not reset it. before is the part's fails count taken ahead of the sample.
void sense_part_lost (sense_part_t *p)
 An online part that answers and no longer holds the set-up written to it: offline, and probed again SENSE_RETRY_MS on.
uint16_t sense_be16 (const uint8_t *b)
 Big-endian register contents, most significant byte first.
uint32_t sense_be24 (const uint8_t *b)
 Read a big-endian 24-bit value.
uint64_t sense_be40 (const uint8_t *b)
 Read a big-endian 40-bit value.
int32_t sense_sext32 (uint32_t v, unsigned bits)
 The low bits of v as a two's complement number (1 to 32 bits).
int64_t sense_sext64 (uint64_t v, unsigned bits)
 The same for up to 64 bits.
int64_t sense_muldiv (int64_t a, uint32_t mul, uint64_t div)
 a × mul / div without the product overflowing, truncated towards zero.

Detailed Description

The sensor bus: one I2C (Inter-Integrated Circuit) bus with current monitors on it, as the drivers see it.

The drivers (ina228.h, ina3221.h) reach the wire through two callbacks, a register read and a register write, each answering with an error code. The IOmcu supplies them from its I2C block; the host suite supplies a fake that answers as each part does. Both parts send the most significant byte first (INA228 SLYS021A §7.5.1, INA3221 SBOS576C §8.2) and keep the register pointer between transactions, so a read is the pointer write and n bytes back, and a write is the pointer and 2 bytes.

What this module decides:

  • Bus stuck. A transaction that finds SDA or SCL held low (SENSE_BUS_LOW), or SENSE_STUCK_TIMEOUTS transactions in a row that do not finish (SENSE_TIMEOUT), mark the bus stuck. A NACK (not acknowledged) does not: the address byte went out and the lines came back, so the bus works and a part is missing. While the bus is stuck nothing is sent; a recovery is due at once and every SENSE_RECOVER_MS after that. The caller runs it – 9 clocks on SCL as GPIO (general-purpose input/output), a STOP, the I2C block re-initialised – and reports it with sense_bus_recovered(). The next transaction then goes out as the test: one that ends with OK or a NACK clears the stuck flag. The INA3221 also frees the bus by itself after SCL is held low for 28ms to 35ms (SBOS576C §6.5, SMBus timeout); the INA228's datasheet states no such timeout.
  • A part online or offline. A part is probed (identity read, set-up written and read back) before anything else is said to it. SENSE_FAILS failed transactions in a row take an online part offline: 6ms of readings at 500Hz. So does a set-up register that reads back another value than the one written, twice in a row: the part reset itself and runs on its power-on set-up (sense_part_lost()). A part that is not online is probed again every SENSE_RETRY_MS. Nothing is acted on here: an offline part is reported, its readings stop, and the bench is not disarmed.

Pure C, no SDK (software development kit): host-tested against a fake bus in test_ina228 and test_ina3221.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ SENSE_FAILS

#define SENSE_FAILS   3u

failed transactions in a row: offline

◆ SENSE_RECOVER_MS

#define SENSE_RECOVER_MS   100u

recoveries this far apart while stuck; chosen, not measured

◆ SENSE_RETRY_MS

#define SENSE_RETRY_MS   1000u

a part not online, probed again

◆ SENSE_STUCK_TIMEOUTS

#define SENSE_STUCK_TIMEOUTS   2u

timeouts in a row: the bus is stuck

Enumeration Type Documentation

◆ sense_clip_t

A reading that may be at the end of its register's range.

Enumerator
SENSE_CLIP_LOW 

at the bottom of the range

SENSE_CLIP_HIGH 

at the top of the range

◆ sense_err_t

What a transaction came to.

The first four come from the wire; the rest are answers given without sending anything.

Enumerator
SENSE_NACK 

address or a byte not acknowledged

SENSE_TIMEOUT 

did not finish in its time: SCL held low

SENSE_BUS_LOW 

SDA or SCL low with the bus idle, or arbitration lost

SENSE_STUCK 

not sent: the bus is stuck, a recovery is owed

SENSE_OFFLINE 

not sent: the part is not online

SENSE_BAD_ARG 

not sent: a channel the part has not enabled

◆ sense_state_t

A part on the bus, as far as it is known.

Enumerator
SENSE_PART_UNSET 

no valid set-up given; nothing is sent

SENSE_PART_UNPROBED 

set up, not probed yet

SENSE_PART_ABSENT 

the probe went unanswered

SENSE_PART_WRONG_ID 

answered with another identity; not used

SENSE_PART_REFUSED 

identity good; the set-up did not read back as written

SENSE_PART_OFFLINE 

was online; SENSE_FAILS transactions in a row failed

Function Documentation

◆ sense_be16()

uint16_t sense_be16 ( const uint8_t * b)

Big-endian register contents, most significant byte first.

Parameters
bTwo bytes, most significant first.
Returns
The value.

◆ sense_be24()

uint32_t sense_be24 ( const uint8_t * b)

Read a big-endian 24-bit value.

Parameters
bThree bytes, most significant first.
Returns
The value.

◆ sense_be40()

uint64_t sense_be40 ( const uint8_t * b)

Read a big-endian 40-bit value.

Parameters
bFive bytes, most significant first.
Returns
The value.

◆ sense_bus_init()

void sense_bus_init ( sense_bus_t * b,
const sense_i2c_t * io )

Set up a bus over a wire.

Parameters
bThe bus state to set up.
ioThe wire callbacks; kept, not copied.

◆ sense_bus_note()

void sense_bus_note ( sense_bus_t * b,
sense_err_t e )

A transaction made on the wire without this module – an address scan – and what it came to, for the stuck decision: a held line or a timeout counts as one of this module's would, and marks the bus stuck by the same rule.

An OK or a NACK shows the lines working and ends a run of timeouts; a NACK is a missing address and is not counted as an error. Any other code reached nothing and changes nothing.

Parameters
bThe bus.
eThe result of the transaction.

◆ sense_bus_read()

sense_err_t sense_bus_read ( sense_bus_t * b,
uint8_t addr,
uint8_t reg,
uint8_t * buf,
size_t n )

A register read: n bytes, most significant first, into buf.

Parameters
bThe bus.
addrI2C address of the part.
regRegister address.
bufReceives n bytes.
nRegister width in bytes.
Returns
SENSE_OK, a wire error, or SENSE_STUCK while the bus is stuck.

◆ sense_bus_recovered()

void sense_bus_recovered ( sense_bus_t * b,
uint32_t now_ms )

The caller has run a recovery.

The next transaction goes out as the test; on a bus that is not stuck this only counts it.

Parameters
bThe bus.
now_msThe current time, in ms.

◆ sense_bus_recovery_due()

bool sense_bus_recovery_due ( const sense_bus_t * b,
uint32_t now_ms )

Whether a recovery is to be run now: the bus is stuck, and none has run since, or the last ran SENSE_RECOVER_MS or more ago without a transaction proving the bus free.

Parameters
bThe bus.
now_msThe current time, in ms.
Returns
true when the caller should run a recovery.

◆ sense_bus_write16()

sense_err_t sense_bus_write16 ( sense_bus_t * b,
uint8_t addr,
uint8_t reg,
uint16_t value )

A 16-bit register write.

Parameters
bThe bus.
addrI2C address of the part.
regRegister address.
valueThe 16-bit value, most significant byte sent first.
Returns
SENSE_OK, a wire error, or SENSE_STUCK while the bus is stuck.

◆ sense_muldiv()

int64_t sense_muldiv ( int64_t a,
uint32_t mul,
uint64_t div )

a × mul / div without the product overflowing, truncated towards zero.

Exact while |a / div × mul| and div × mul fit 63 bits.

Parameters
aThe multiplicand.
mulThe multiplier.
divThe divisor, not zero.
Returns
a × mul / div, truncated towards zero.

◆ sense_part_init()

void sense_part_init ( sense_part_t * p,
sense_bus_t * bus,
uint8_t addr )

Set up a part record on a bus.

Parameters
pThe part record.
busThe bus the part sits on.
addrI2C address of the part.

◆ sense_part_lost()

void sense_part_lost ( sense_part_t * p)

An online part that answers and no longer holds the set-up written to it: offline, and probed again SENSE_RETRY_MS on.

Nothing for a part that is not online.

Parameters
pThe part record.

◆ sense_part_probe_due()

bool sense_part_probe_due ( sense_part_t * p,
uint32_t now_ms )

Every tick, with the time: whether the part is to be probed now.

Parameters
pThe part record.
now_msThe current time, in ms.
Returns
true when the part is due a probe.

◆ sense_part_probed()

void sense_part_probed ( sense_part_t * p,
sense_state_t state )

The probe's result; a part left anything but online is probed again SENSE_RETRY_MS on.

Parameters
pThe part record.
stateThe state the probe reached.

◆ sense_part_read()

sense_err_t sense_part_read ( sense_part_t * p,
uint8_t reg,
uint8_t * buf,
size_t n )

A read or write of an online part, counted towards taking it offline.

A part that is not online is not addressed: SENSE_OFFLINE.

Parameters
pThe part record.
regRegister address.
bufReceives n bytes.
nRegister width in bytes.
Returns
SENSE_OK, SENSE_OFFLINE, or the bus error.

◆ sense_part_sample_failed()

void sense_part_sample_failed ( sense_part_t * p,
uint8_t before )

After a sample of two reads whose second failed: the failure run is before plus one, as if the first read had not reset it. before is the part's fails count taken ahead of the sample.

A part that the failed read took offline is left as it is.

Parameters
pThe part record.
beforeThe part's fails count taken ahead of the sample.

◆ sense_part_write16()

sense_err_t sense_part_write16 ( sense_part_t * p,
uint8_t reg,
uint16_t value )

Write a 16-bit register of a part, counted towards taking it offline.

Parameters
pThe part record.
regRegister address.
valueThe 16-bit value.
Returns
SENSE_OK, SENSE_OFFLINE, or the bus error.

◆ sense_sext32()

int32_t sense_sext32 ( uint32_t v,
unsigned bits )

The low bits of v as a two's complement number (1 to 32 bits).

Two's complement by subtraction rather than a signed shift, which C leaves to the implementation for negative numbers.

Parameters
vThe value.
bitsWidth of the field, 1 to 32.
Returns
The signed value.

The low bits of v as a two's complement number (1 to 32 bits).

◆ sense_sext64()

int64_t sense_sext64 ( uint64_t v,
unsigned bits )

The same for up to 64 bits.

Parameters
vThe value.
bitsWidth of the field, 1 to 64.
Returns
The signed value.