|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
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. | |
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
| #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.
| #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.
| #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.
| #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.
| void heartbeat_gen_init | ( | heartbeat_gen_t * | g | ) |
Start held low: nothing downstream may run until this loop asks for it.
| g | Generator state to initialise. |
| 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.
| g | Generator state. |
| now_ms | Current time, in milliseconds. |
| alive | true when the caller judges the bench fit to run. |
| 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.
| m | Monitor state. |
| now_ms | Current time, in milliseconds. |
| 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.
| m | Monitor state. |
| now_ms | Time of the edge, in milliseconds. |
| void heartbeat_mon_init | ( | heartbeat_mon_t * | m | ) |
Start not alive, with no edge seen.
| m | Monitor state to initialise. |