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

The safety heartbeat: the panel's signal that it is running, and the IOmcu's judgement of whether to believe it. More...

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

Go to the source code of this file.

Data Structures

struct  heartbeat_gen_t
struct  heartbeat_mon_t

Macros

#define HEARTBEAT_PERIOD_MS   20u
 How often the panel asks for an edge.
#define HEARTBEAT_MIN_GAP_MS   4u
 The interval the monitor will accept between two edges.
#define HEARTBEAT_MAX_GAP_MS   150u
#define HEARTBEAT_GOOD_RUN   4u
 Consecutive good intervals before the line is called alive.
#define HEARTBEAT_SETTLE_MS   (HEARTBEAT_GOOD_RUN * HEARTBEAT_PERIOD_MS + HEARTBEAT_PERIOD_MS)
 The least time the panel gives a line that was already running before it writes an arm: HEARTBEAT_GOOD_RUN intervals, plus one, 100ms.

Functions

void heartbeat_gen_init (heartbeat_gen_t *g)
 Start held low: nothing downstream may run until this loop asks for it.
bool heartbeat_gen_step (heartbeat_gen_t *g, uint32_t now_ms, bool alive)
 Advance the generator; returns the level the pin should be driven to.
void heartbeat_mon_init (heartbeat_mon_t *m)
 Start not alive, with no edge seen.
void heartbeat_mon_edge (heartbeat_mon_t *m, uint32_t now_ms)
 Record an edge on the safety line.
bool heartbeat_mon_alive (heartbeat_mon_t *m, uint32_t now_ms)
 Whether the line may be believed now.

Detailed Description

The safety heartbeat: the panel's signal that it is running, and the IOmcu's judgement of whether to believe it.

A level cannot carry this. A GPIO (general-purpose input/output) held high is also what a crashed processor, a shorted pin and an unpowered board produce. A running panel is one whose output changes, so the safety line carries edges and the IOmcu's outputs sit behind a retriggerable monostable that stays energised only while edges keep arriving.

The monostable is a backstop, not the mechanism. It cannot tell a heartbeat from noise: anything that edges fast enough retriggers it, and a shorted or ringing line edges fast. So the IOmcu checks the period in firmware too. Both ends live in one file because they share the numbers.

The monitor is asymmetric:

slow to trust    -- several consecutive well-spaced edges before the
                    line is called alive, so a burst of noise or a
                    single edge at power-on cannot enable an output
instant to doubt -- one bad interval, or one silent window, and it is
                    dead again immediately

The inverse, a line that enables on a glitch and hesitates to disable, is not a safety interlock.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ HEARTBEAT_GOOD_RUN

#define HEARTBEAT_GOOD_RUN   4u

Consecutive good intervals before the line is called alive.

Four, which is two full toggles: enough that a glitch pair cannot do it, few enough that arming does not wait a perceptible time. Four intervals at HEARTBEAT_PERIOD_MS is 80ms.

◆ HEARTBEAT_MIN_GAP_MS

#define HEARTBEAT_MIN_GAP_MS   4u

The interval the monitor will accept between two edges.

The floor rejects noise: a line that is ringing, shorted to a clock, or driven by something other than the panel edges far faster than a 20ms period can, and the monostable alone would happily be retriggered by it. The ceiling leaves room above that period for a panel whose control task is held up – a panel that has not edged for 150ms has stopped running.

◆ HEARTBEAT_PERIOD_MS

#define HEARTBEAT_PERIOD_MS   20u

How often the panel asks for an edge.

The panel toggles from its control task, which runs every 5ms, so an edge asked for at 20ms is delivered within one of those passes and the line edges every 20ms. The generator does not have to know the caller's rate: it is asked on every pass and answers when the period is up.

◆ HEARTBEAT_SETTLE_MS

#define HEARTBEAT_SETTLE_MS   (HEARTBEAT_GOOD_RUN * HEARTBEAT_PERIOD_MS + HEARTBEAT_PERIOD_MS)

The least time the panel gives a line that was already running before it writes an arm: HEARTBEAT_GOOD_RUN intervals, plus one, 100ms.

It is not enough for a line that was withheld. The generator's first edge is one period after it resumes and the monitor rejects the interval that edge closes, so the fifth edge is what makes the line trusted: 100 to 125ms after the resume at a 5ms pass, later on a slower one. arming_set_line_wait() covers the rest by asking the far end.

Function Documentation

◆ heartbeat_gen_init()

void heartbeat_gen_init ( heartbeat_gen_t * g)

Start held low: nothing downstream may run until this loop asks for it.

Parameters
gGenerator state to initialise.

◆ heartbeat_gen_step()

bool heartbeat_gen_step ( heartbeat_gen_t * g,
uint32_t now_ms,
bool alive )

Advance the generator; returns the level the pin should be driven to.

alive is the caller's own judgement of whether it is fit to keep the bench running – touch answering, link up, no latched stop. When it is false the line goes low immediately rather than merely stopping: waiting for the monostable to time out would spend its whole period still enabled, and the fastest correct thing to do with a stop is all of it at once.

Call it as often as you like; it edges no faster than HEARTBEAT_PERIOD_MS.

Parameters
gGenerator state.
now_msCurrent time, in milliseconds.
alivetrue when the caller judges the bench fit to run.
Returns
The level the pin is driven to.

◆ heartbeat_mon_alive()

bool heartbeat_mon_alive ( heartbeat_mon_t * m,
uint32_t now_ms )

Whether the line may be believed now.

Must be called even when no edges are arriving: silence is the failure this exists to catch, and silence generates no events. Calling it is what notices that nothing has happened.

Parameters
mMonitor state.
now_msCurrent time, in milliseconds.
Returns
true when the line may be believed now.

◆ heartbeat_mon_edge()

void heartbeat_mon_edge ( heartbeat_mon_t * m,
uint32_t now_ms )

Record an edge on the safety line.

Called from wherever edges are noticed – an interrupt, or a poll that samples faster than HEARTBEAT_MIN_GAP_MS. It only ever costs a subtraction and a compare, so an ISR is a fine place for it.

Parameters
mMonitor state.
now_msTime of the edge, in milliseconds.

◆ heartbeat_mon_init()

void heartbeat_mon_init ( heartbeat_mon_t * m)

Start not alive, with no edge seen.

Parameters
mMonitor state to initialise.