Skip to content
Merged
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
9 changes: 8 additions & 1 deletion Inc/eeprom.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,14 @@ typedef union EEprom_u {
uint8_t filter_hz; // 181
uint8_t debug_rate; // 182
uint8_t term_enable; // 183
uint8_t reserved[8]; // 184-191
/* Width in C of the thermal foldback ramp below full
* derate; the onset is limits.temperature. Lives in the
* can block because that is where the free bytes are -
* the limiter itself is target-agnostic, and 0xFF on a
* board that never wrote this page is coerced in
* settings.c. */
uint8_t temp_derate_band; // 184
uint8_t reserved[7]; // 185-191
} can;
};
uint8_t buffer[192];
Expand Down
22 changes: 22 additions & 0 deletions Inc/motor_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ extern fastPID stallPid;
extern char use_speed_control_loop;
extern char use_current_limit;
extern int16_t use_current_limit_adjust;
/* Protection ceilings (duty units) + the filtered die temperature the
* thermal one derates from. See runtimeThermalLimitTick() / setInput(). */
extern volatile uint16_t thermal_duty_ceiling;
extern volatile uint16_t duty_limit_ceiling;
extern volatile int16_t degrees_celsius_filtered;
/*
* Thermal foldback ramp width, in C below full derate. The onset is
* eepromBuffer.limits.temperature; authority falls linearly from there to
* THERMAL_CEIL_FLOOR over this many degrees. Tunable (DroneCAN
* TEMP_DERATE_BAND) because the right slope depends on how hard the
* airframe loads the ESC: a wide band trades a longer partial-power
* excursion for a gentler thrust change, a narrow one the reverse.
*
* Default 15 C keeps the whole ramp inside the die sensor's useful range
* on ARK_G431_CAN: the G4 factory calibration points are 30 C and 110 C
* (stm32g4xx_ll_adc.h), so 105 -> 120 C only extrapolates over the last
* 10 C, and full derate lands below the part's 125 C junction limit.
*/
#define THERMAL_DERATE_BAND_DEFAULT 15
#define THERMAL_DERATE_BAND_MIN 5
#define THERMAL_DERATE_BAND_MAX 40
extern uint8_t temp_derate_band_c;
extern int32_t input_override;
extern int32_t stall_protection_adjust;
extern uint16_t stall_protect_target_interval;
Expand Down
4 changes: 4 additions & 0 deletions Inc/runtime_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ void runtimeSendTelemetryIfNeeded(void);
/* ADC sample + battery LVC + optional ADC input path. */
void runtimeProcessAdcAndProtections(void);

/* Filter the die temperature and recompute thermal_duty_ceiling. Must be
* called at 1 kHz (from the ADC block) - the IIR time constant assumes it. */
void runtimeThermalLimitTick(void);

/*
* Brushless running path (limits, filter, stall) or sine/stepper branch,
* matching the previous if (stepper_sine == 0) / else structure.
Expand Down
40 changes: 38 additions & 2 deletions Inc/targets.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,20 @@
* ramp/governor work was ported to this target. */
# define RAMP_SPEED_LOW_RPM 3
# define RAMP_SPEED_HIGH_RPM 8
/* Unconfigured-eeprom max_ramp default — same 2 %/ms as 4IN1 / factory JSON. */
# define TARGET_DEFAULT_MAX_RAMP 20
/* Unconfigured-eeprom max_ramp default; matches the factory JSON. 5 = fine
* mode, 0.5 %/ms, full scale in 200 ms - what larger 12S ESCs ship
* (APD/Hargrave default 50 % per 100 ms) rather than the 4IN1's 2.0 %/ms.
* Fine mode (<10) applies to every regime including startup, so spool-up
* slews at this rate too; that is the part to watch on the bench. */
# define TARGET_DEFAULT_MAX_RAMP 5
/* Protection envelope a DroneCAN "restore defaults" must land on. Same
* values as factory/ARK_G431_CAN_eeprom_defaults.json, enforced by
* scripts/check-erase-defaults.py: an erase has to leave the ESC with the
* protection it shipped with, not the AM32 configurator's disabled pair.
* 105 C foldback onset over a 15 C band, 100 = 200 A. */
# define TARGET_DEFAULT_TEMPERATURE_LIMIT 105
# define TARGET_DEFAULT_CURRENT_LIMIT 100
# define TARGET_DEFAULT_TEMP_DERATE_BAND 15
/* Closed-loop earlier at low RPM (same bench rationale as ARK_4IN1_F051). */
# ifndef POLLING_MODE_THRESHOLD
# define POLLING_MODE_THRESHOLD 5000
Expand Down Expand Up @@ -284,6 +296,30 @@
# define TARGET_DEFAULT_MAX_RAMP 160
#endif

/*
* Protection defaults restored by a DroneCAN param ERASE, and the single
* source of truth for default_settings[] bytes 43/44 plus the
* post-skeleton band byte (184). These must equal what the product's
* factory eeprom defaults JSON ships, or "restore defaults" in the field
* silently changes a shipped ESC's protection envelope - the one config
* change nobody re-checks afterwards. check-factory-image-ark.sh gates the
* JSON against the built image; scripts/check-erase-defaults.py gates
* these macros against the JSON.
*
* Values are the eeprom storage encoding: temperature in C, current in
* 2 A counts (so 100 = 200 A, the largest settings.c will arm), band in C.
* 255 in the temperature slot means "no thermal derate".
*/
#ifndef TARGET_DEFAULT_TEMPERATURE_LIMIT
# define TARGET_DEFAULT_TEMPERATURE_LIMIT 255
#endif
#ifndef TARGET_DEFAULT_CURRENT_LIMIT
# define TARGET_DEFAULT_CURRENT_LIMIT 0
#endif
#ifndef TARGET_DEFAULT_TEMP_DERATE_BAND
# define TARGET_DEFAULT_TEMP_DERATE_BAND THERMAL_DERATE_BAND_DEFAULT
#endif

#ifndef RAMP_SPEED_STARTUP
# define RAMP_SPEED_STARTUP 2 // adjusted 2.14 to match duty cycle change between mcu targets.
#endif
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ factory-image-check: factory-image
FACTORY_DEFAULTS=$(FACTORY_G431_DEFAULTS) \
BL_IMAGE="$(BL_IMAGE_G431_CAN)" \
bash scripts/check-factory-image-ark.sh
$(QUIET)$(ECHO) "--- erase-defaults check ---"
$(QUIET)python3 scripts/check-erase-defaults.py $(FACTORY_G431_PRODUCT)

# Code formatting (clang-format ≈ PX4 astyle/Linux look; see .clang-format).
# Same target names as PX4:
Expand Down
24 changes: 23 additions & 1 deletion Mcu/SITL/Src/sitl_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
u8 ramp_divider, u8 max_ramp_startup_vcomp, u8 acq_resist,
u8 desync_episode_bucket (v6 ramp settings +
episode observability)
u16 thermal_duty_ceiling, u16 current_duty_ceiling,
u16 duty_limit_ceiling, i16 degrees_celsius,
i16 degrees_celsius_filtered, i16 actual_current (v7 protection
ceilings)
Fields are only ever APPENDED and the version is bumped; clients
that unpack a shorter prefix keep working unchanged (they
length-check with >=).
Expand Down Expand Up @@ -608,9 +612,19 @@ void sitl_state_poll(void)
uint8_t max_ramp_startup_vcomp_v;
uint8_t acq_resist_v;
uint8_t desync_episode_bucket_v;
/* v7: protection ceilings. Both are smooth derates that
* only ever LOWER duty, so a test cannot tell them from a
* weak plant by watching rpm alone - it has to read the
* ceiling that produced the derate. */
uint16_t thermal_duty_ceiling_v;
uint16_t current_duty_ceiling_v;
uint16_t duty_limit_ceiling_v;
int16_t degrees_celsius_v;
int16_t degrees_celsius_filt_v;
int16_t actual_current_v;
} reply = {
.magic = 0x5356,
.version = 6,
.version = 7,
.zero_crosses = zero_crosses,
.commutation_interval = commutation_interval,
.dropped_edges = motor_zc_dropped(),
Expand Down Expand Up @@ -643,6 +657,14 @@ void sitl_state_poll(void)
.max_ramp_startup_vcomp_v = max_ramp_startup_vcomp,
.acq_resist_v = fault_acq_resist_events,
.desync_episode_bucket_v = desync_episode_bucket,
.thermal_duty_ceiling_v = thermal_duty_ceiling,
/* 2000 when the limiter is disabled, so the field reads as
* "no cap" rather than as a stale integrator value. */
.current_duty_ceiling_v = use_current_limit ? (uint16_t)use_current_limit_adjust : 2000u,
.duty_limit_ceiling_v = duty_limit_ceiling,
.degrees_celsius_v = degrees_celsius,
.degrees_celsius_filt_v = degrees_celsius_filtered,
.actual_current_v = actual_current,
};
sendto(fd, &reply, sizeof(reply), 0, (struct sockaddr *)&src, sizeof(src));
} else if (cmd == 10 && ret >= 8) {
Expand Down
6 changes: 4 additions & 2 deletions Mcu/SITL/sitl_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ def _version_defines():
(40, 'SINE_MODE_CHANGEOVER', 0, 255, 5, 'throttle level leaving sine start'),
(41, 'DRAG_BRAKE_STRENGTH', 0, 10, 10, 'brake strength at zero throttle'),
(42, 'DRIVING_BRAKE_STRENGTH', 0, 10, 10, 'brake strength while driving'),
(43, 'TEMPERATURE_LIMIT', 70, 255, 255, 'thermal limit, C (255 = off)'),
(44, 'CURRENT_LIMIT', 0, 100, 102, 'current limit in 2A steps (>100 = off)'),
(43, 'TEMPERATURE_LIMIT', 70, 255, 105, 'derate onset, C (outside 70..140 = off)'),
(44, 'CURRENT_LIMIT', 0, 100, 100, 'current limit in 2A steps (>100 = off)'),
(45, 'SINE_MODE_POWER', 1, 10, 5, 'sine startup power'),
(46, 'INPUT_SIGNAL_TYPE', 0, 5, 5, '0=auto 1=dshot 2=servo 5=dronecan only'),
(47, 'AUTO_ADVANCE', 0, 1, 0, 'automatic timing advance'),
Expand All @@ -86,6 +86,7 @@ def _version_defines():
(181, 'FILTER_HZ', 0, 255, 20, 'DroneCAN input filter cutoff, Hz'),
(182, 'DEBUG_RATE', 0, 200, 0, 'FlexDebug rate, Hz'),
(183, 'TERM_ENABLE', 0, 1, 0, 'serial terminal'),
(184, 'TEMP_DERATE_BAND', 5, 40, 15, 'C from thermal onset to full derate'),
]

PARAMS_BY_NAME = dict((p[1], p) for p in PARAMS)
Expand Down Expand Up @@ -114,6 +115,7 @@ def _onoff(v):
if v < 3 else '?',
'CURRENT_LIMIT': lambda v: '%d A' % (v * 2) if v <= 100 else 'off',
'TEMPERATURE_LIMIT': lambda v: '%d C' % v if 70 <= v <= 140 else 'off',
'TEMP_DERATE_BAND': lambda v: '%d C ramp' % v if 5 <= v <= 40 else '15 C (default)',
'TELEM_RATE': lambda v: '%d Hz' % v,
'DEBUG_RATE': lambda v: '%d Hz' % v,
'FILTER_HZ': lambda v: '%d Hz' % v,
Expand Down
Loading
Loading