Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Inc/faults.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,34 @@ void faultDesyncEpisodeTick1kHz(void);
/* True while a post-desync coast is mandatory (caller must not re-start). */
uint8_t faultDesyncRestartHoldoffActive(void);

/*
* Monotonic since-boot count of stall-rail trips on an ESTABLISHED run - the
* same zero_crosses > 100 gate the episode charge uses, so the many legitimate
* kicks a heavy prop needs at low throttle are not counted as errors.
*
* volatile: read from the main loop (telemetry) while the grind rail that
* forces this trip runs in tenKhzRoutine (ISR context).
*/
extern volatile uint32_t fault_stall_trips;

/*
* Total hard-error events since boot, for uavcan.equipment.esc.Status
* error_count ("errors since start-up").
*
* Sums the two PRIMITIVE run-killers, which are mutually exclusive:
* - desync_happened : the jump-desync check in runtimeProcessDesyncCheck
* - fault_stall_trips : the INTERVAL_TIMER stall rail
*
* Deliberately NOT additional addends, because each already funnels into the
* stall rail and would double-count one physical failure:
* - blind-grind rail (faultDesyncEpisodeTick1kHz) kicks INTERVAL_TIMER to
* 46000 so the stall rail is guaranteed to trip on the next main pass
* - blind-step / miss-bucket limit (bemf_zc.c) hands off the same way
* - the episode-rail latch is a CONSEQUENCE of accumulating the above, and
* is a state (ESC_FAULT_STUCK), not an independent event
* Attribution between those sub-causes belongs in the AM32-reserved FlexDebug
* payload, not in this scalar.
*/
uint32_t faultErrorCount(void);

#endif /* FAULTS_H_ */
7 changes: 7 additions & 0 deletions Mcu/SITL/tests/test_dronecan.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def test_dronecan_throttle_and_esc_status(sitl_can_factory, state_stream, mcast_
def on_esc(e):
status['rpm'] = e.message.rpm
status['voltage'] = e.message.voltage
status['errors'] = e.message.error_count
status['count'] = status.get('count', 0) + 1

node.add_handler(dronecan.uavcan.equipment.esc.Status, on_esc)
Expand All @@ -59,6 +60,12 @@ def on_esc(e):
assert 3500 <= status.get('rpm', -1) <= 6500, status
assert 15 < status.get('voltage', 0) < 18, status
assert status.get('count', 0) >= 3, 'too few esc.Status: %s' % status
# error_count aggregates hard-error events (jump desync + stall rail).
# An honest spool-up to a steady 4-6k rpm must report none: the stall
# rail is gated on zero_crosses > 100 precisely so the kicks a normal
# start needs are not counted as errors.
assert status.get('errors', -1) == 0, \
'healthy run reported error_count=%s' % status.get('errors')
finally:
# stop motor
for _ in range(10):
Expand Down
9 changes: 6 additions & 3 deletions Src/DroneCAN/DroneCAN.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# include "signal.h"
# include "version.h"
# include "eeprom.h"
# include "faults.h"
# include <stdarg.h>
# include <stdio.h>
# include <string.h>
Expand Down Expand Up @@ -107,7 +108,6 @@ extern volatile char armed;
extern volatile uint32_t commutation_interval;
extern uint8_t auto_advance_level;
extern uint16_t low_cell_volt_cutoff;
extern uint32_t desync_happened;

static uint16_t last_can_input;
static uint64_t last_heartbeat_us;
Expand Down Expand Up @@ -1006,8 +1006,11 @@ static void send_ESCStatus(void)
struct uavcan_equipment_esc_Status pkt;
uint8_t buffer[UAVCAN_EQUIPMENT_ESC_STATUS_MAX_SIZE];

// make up some synthetic status data
pkt.error_count = desync_happened; // fill desync count here
/* Hard-error events since boot. Was desync_happened alone, which missed
* every run killed by the stall rail (and so also the blind-grind and
* blind/miss-limit paths that funnel into it) - a grinding motor
* reported error_count 0. See faultErrorCount(). */
pkt.error_count = faultErrorCount();
pkt.voltage = battery_voltage * 0.01;

pkt.current = (current.sum / (float)current.count) * 0.01;
Expand Down
14 changes: 14 additions & 0 deletions Src/faults.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ extern volatile uint16_t zero_input_count;
extern volatile uint32_t dma_buffer[64];
extern void resetInputCaptureTimer(void);

/* See the comments on the declarations in faults.h. */
volatile uint32_t fault_stall_trips = 0;

uint32_t faultErrorCount(void)
{
return desync_happened + fault_stall_trips;
}

uint8_t faultHandleStuckRotorIfNeeded(void)
{
#ifndef BRUSHED_MODE
Expand Down Expand Up @@ -327,6 +335,12 @@ void faultHandleBemfIntervalStall(void)
// this rail is guaranteed to run next pass): blind stepping only
// arms at zero_crosses >= 100, so those episodes always charge.
if (zero_crosses > 100) {
/* Established run died here. This is the ONLY place the
* stall rail is counted, and it is the aggregation point
* for the grind rail and the blind/miss-limit handoff,
* both of which reach the loop through this trip - see
* faultErrorCount(). */
fault_stall_trips++;
faultDesyncEpisodeCharge(DESYNC_EPISODE_STALL_RAIL);
}
if (escIsFault()) {
Expand Down
Loading