Files
hpsg5-controller_v2-stm32g4/Core/Advance_Control/pwm.h

307 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file pwm.h (variant/T06235/compact_src)
* @brief Compact single-header API for the T06235 PWM controller.
*
* Variant note: this header was copied verbatim from variant/T06215/compact_src
* per the pillar-port-verbatim rule (docs/pillar-functions.md). T06235 is a
* recompile of T06215 with a +0x14 cal-block insertion and one RAM slot move
* (reset_flag 0x002e → 0x002c) — see variant/T06235/docs/algorithm-diff-vs-T06215.md.
* Runtime-struct field names are purely semantic; the address binding for
* any field lives in `pwm_addr_map.h` (documentation-only; not included
* by pwm.c).
*
* Public API:
* pwm_init() — one-time setup
* pwm_service() — per-cycle update (5-stage pipeline)
*
* External inputs arrive through a getter vtable; callbacks return one
* signal in native PWM units.
*/
#ifndef PWM_H
#define PWM_H
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
/* ── MCS-96 arithmetic primitives ───────────────────────────────────── */
#define MUL_S16(a, b) ((int32_t)(int16_t)(a) * (int32_t)(int16_t)(b))
#define MULU_U16(a, b) ((uint32_t)(uint16_t)(a) * (uint32_t)(uint16_t)(b))
#define CLAMP(v, lo, hi) ((v) < (lo) ? (lo) : (v) > (hi) ? (hi) : (v))
static inline int16_t shra16(int16_t v, unsigned n) {
if (!n) return v;
uint16_t u = (uint16_t)v, sign = (u >> 15) & 1u;
uint16_t m = sign ? (uint16_t)(((uint16_t)~0u) << (16u - n)) : 0u;
return (int16_t)((u >> n) | m);
}
static inline int32_t shra32(int32_t v, unsigned n) {
if (!n) return v;
uint32_t u = (uint32_t)v, sign = (u >> 31) & 1u;
uint32_t m = sign ? (~(uint32_t)0u << (32u - n)) : 0u;
return (int32_t)((u >> n) | m);
}
/* ══════════════════════════════════════════════════════════════════════
* 1D INTERPOLATION SLOT
* ══════════════════════════════════════════════════════════════════════ */
typedef struct pwm_interp_slot {
int16_t row_stride;
int16_t x_interval;
int16_t x_offset;
int16_t y_byte_off;
} pwm_interp_slot_t;
/* ══════════════════════════════════════════════════════════════════════
* EXTERNAL INPUTS
* ══════════════════════════════════════════════════════════════════════ */
typedef struct pwm_inputs {
int16_t ckp_in; /* sensed position */
uint16_t rpm; /* engine RPM */
int16_t angle_dec_cmd; /* accepted for cross-family API parity; unused here */
int16_t inj_qty_demand; /* CAN: injection-quantity demand */
int16_t b_fb_kw; /* CAN: plunger feedback baseline (drives target) */
int16_t cl_gate_input; /* CAN: open/closed-loop discriminant (was state_130) */
uint16_t supply_voltage; /* shape_eval submap input */
int16_t temperature; /* accepted for cross-family API parity; unused here */
} pwm_inputs_t;
typedef struct pwm_input_getters {
int16_t (*ckp_in) (void *ctx);
uint16_t (*rpm) (void *ctx);
int16_t (*angle_dec_cmd) (void *ctx);
int16_t (*inj_qty_demand)(void *ctx);
int16_t (*b_fb_kw) (void *ctx);
int16_t (*cl_gate_input) (void *ctx);
uint16_t (*supply_voltage)(void *ctx);
int16_t (*temperature) (void *ctx);
void *ctx;
} pwm_input_getters_t;
typedef struct pwm_calibration pwm_calibration_t;
typedef struct pwm_submap_descr pwm_submap_descr_t;
/* ══════════════════════════════════════════════════════════════════════
* RUNTIME STATE
* Field names are purely semantic. ROM-address bindings live in
* pwm_addr_map.h (documentation-only).
* ══════════════════════════════════════════════════════════════════════ */
typedef struct pwm_runtime {
/* External inputs — refreshed each cycle from getters */
pwm_inputs_t inputs;
/* Async flags. */
uint8_t reset_flag;
uint8_t system_flags_110; /* bit 5 = OL force; bit 0 = recovery latch */
/* ── Setpoint architecture ── */
/* target_5e: PRIMARY CAN-decoded setpoint (RW5E in t06215). */
int16_t target_5e;
/* pi_high_clamp_ceiling: SECONDARY RPM-derived ceiling — written each
* cycle by pwm_service from the setpoint submap interp; consumed as
* the PI integrator output upper-clamp inside s_pi_update and the
* supervisor error ceiling. (DAT_0344 in t06215; was misnamed
* target_336 in the original fork from t06211.) */
int16_t pi_high_clamp_ceiling;
/* CAN-staging buffers — written before pwm_service so s_setpoint_can_decode
* can transform them into target_5e. */
int16_t can_raw_b_fb_kw;
int16_t can_aux_12e;
int16_t can_half_12a;
int16_t setpoint_offset; /* runtime copy of cal->setpoint_offset */
int16_t rw42_state;
int16_t angle_error_raw; /* DAT_02f0 — supervisor's published error */
int16_t pi_integ_hi_snapshot; /* DAT_02ec — supervisor reset stash; dead store but kept for parity */
uint16_t cl_enable_counter; /* DAT_02ee */
/* ── CL correction ── */
int16_t cl_correction_raw;
int16_t angle_offset;
int16_t supervisor_state;
int16_t pos_error_normalizer; /* runtime copy of cal->pos_error_normalizer */
int16_t neg_error_normalizer; /* runtime copy of cal->neg_error_normalizer */
/* ── Publish + PI ── */
int16_t estimated_angle;
int16_t angle_error_pi; /* target_5e estimated_angle (DAT_0278) */
int16_t active_request; /* PI output = pre-clamp output (RW46 + DAT_0274) */
/* PI flag byte (DAT_028c). Bit layout (s_pi_update @ 0x542f):
* bit 0 = open-loop indicator (consumed by integrator gate)
* bit 1 = P-shape large-error arm taken
* bit 2 = output clamped to pi_low_clamp this cycle
* bit 3 = output clamped to pi_high_clamp_ceiling this cycle
* bit 4 = error > large_pos_error_thresh this cycle
* bit 5 = error < large_neg_error_thresh this cycle
* bits 6,7 = previous cycle's bits 4,5 (rotated by trailing op) */
uint8_t pi_shape_flag;
/* PI working state used by s_pi_update */
int16_t pi_p_term; /* DAT_0276 — output of P-shape segment */
int16_t pi_p_gain_active; /* DAT_0286 — selected gain for integrator step */
int16_t pi_preclamp_out; /* DAT_0274 — pre-publish active_request */
/* PI integrator pair {hi:lo} = {DAT_028a:DAT_0288} */
int16_t pi_integ_lo;
int16_t pi_integ_hi;
/* P-shape segment bounds (boot-init from cal+0x10A/0x10C). */
int16_t p_shape_bound_pos;
int16_t p_shape_bound_neg;
/* P-shape gains and integrator steps (boot-init from cal). */
int16_t p_gain_normal; /* DAT_0454 — normal-range P-term multiplier */
int16_t integ_step_normal; /* DAT_0456 — normal-range integrator multiplier */
int16_t p_slope_large_pos; /* DAT_027e */
int16_t p_slope_large_neg; /* DAT_0280 */
int16_t integ_step_large_pos; /* DAT_0282 */
int16_t integ_step_large_neg; /* DAT_0284 */
int16_t open_loop_p_gain; /* DAT_033e (byte, boot-clamped to 15) */
int16_t pi_state_118; /* recovery counter */
int16_t pi_state_c2; /* cooldown counter */
/* ── PWM output ── */
uint16_t pwm_duty;
uint16_t pwm_on_time;
uint16_t pwm_off_time;
uint16_t pwm_period;
uint8_t pwm_duty_range_flag;
pwm_interp_slot_t pwm_slot_a;
pwm_interp_slot_t pwm_slot_b;
int16_t pwm_shape_state[6];
int16_t pwm_slew_increment;
/* ── Bindings (set once by pwm_init) ── */
const pwm_calibration_t *bound_cal;
const pwm_input_getters_t *bound_getters;
} pwm_runtime_t;
/* ── Calibration (decoded ROM values) ───────────────────────────────── */
struct pwm_calibration {
/* PI controller error-band thresholds */
int16_t large_pos_error_thresh; /* cal+0x10E */
int16_t large_neg_error_thresh; /* cal+0x110 */
int16_t pi_low_clamp; /* cal+0x128 — output low clamp + open-loop lower bound */
int16_t pi_high_clamp; /* fallback upper clamp; runtime uses pi_high_clamp_ceiling */
/* CAN-decoded setpoint (FUN_64c3 family) cal constants */
int16_t b_fb_kw_upper_bound; /* cal+0x004 */
int16_t b_fb_kw_lower_bound; /* cal+0x006 */
int16_t setpoint_offset; /* = cal+0x4c cal+0x4e */
int16_t target_5e_min_clamp; /* cal+0x12A */
int16_t can_aux_12e_max; /* cal+0x002 */
/* CKP-zero acquisition (FUN_55e0 + FUN_6a4b chain) cal constants */
int16_t ckp_zero_anchor; /* cal+0x050 — FUN_6a4b additive anchor */
int16_t can_dckp_offset_bias; /* cal+0x052 — FUN_55e0/FUN_6a4b bias */
int16_t ckp_modulus; /* cal+0x0A2 — FUN_6a4b modulo wrap */
/* Recovery / sustained-error machinery */
int16_t pi_state_c2_reload; /* cal+0x114 — reload value for pi_state_c2 on latch */
int16_t inj_qty_thresh; /* cal+0x116 — inj-qty threshold for recovery vs reset */
int16_t pi_sat_count_threshold; /* cal+0x112 — recovery counter latch threshold */
int16_t rpm_threshold_recovery; /* cal+0x126 — s_recovery RPM gate (was wrongly cal+0x11E in t06211 idiom) */
int16_t pi_cl_rpm_floor; /* flash[0x605C] — s_pi_update OL gate threshold */
/* PI runtime-reset values (boot-derived in ROM; cal-resident here so
* each variant carries its own values without code edits). */
int16_t init_p_shape_bound_pos; /* cal+0x10A — copied to rt->p_shape_bound_pos */
int16_t init_p_shape_bound_neg; /* cal+0x10C — copied to rt->p_shape_bound_neg */
int16_t init_p_gain_normal; /* cal+0x118 — copied to rt->p_gain_normal */
int16_t init_integ_step_normal; /* cal+0x11E — copied to rt->integ_step_normal */
int16_t init_p_slope_large_pos; /* cal+0x11A — copied to rt->p_slope_large_pos */
int16_t init_p_slope_large_neg; /* cal+0x11C — copied to rt->p_slope_large_neg */
int16_t init_integ_step_large_pos;/* cal+0x120 — copied to rt->integ_step_large_pos */
int16_t init_integ_step_large_neg;/* cal+0x122 — copied to rt->integ_step_large_neg */
int16_t init_open_loop_p_gain; /* cal+0x124 byte, clamped to 15 — copied to rt->open_loop_p_gain */
/* CL correction normalizers (RAM in ROM, cal-resident here for parity). */
int16_t init_pos_error_normalizer;
int16_t init_neg_error_normalizer;
/* PWM stage scalars */
int16_t pwm_detail_x0;
int16_t pwm_detail_x1;
int16_t pwm_cached_ptr_0F2;
int16_t pwm_cached_ptr_102;
int16_t pwm_const_104;
int16_t pwm_rpm_windows[8];
int16_t pwm_window_halfwidth;
int16_t pwm_slew_step;
const int16_t *pwm_y_table;
const int16_t *shape_y_table;
int16_t closed_loop_gain_const;
uint16_t pwm_period_min;
uint16_t pwm_period_max;
uint16_t pwm_min;
uint16_t pwm_max;
};
/* ── Submap descriptor ──────────────────────────────────────────────── */
struct pwm_submap_descr {
uint16_t flags;
const int16_t *input_ptr;
uint16_t count;
const int16_t *x;
uint16_t input_addr;
};
static inline void pwm_bind_submap_inputs(
pwm_runtime_t *rt,
pwm_submap_descr_t *descrs,
uint16_t n)
{
for (uint16_t i = 0; i < n; i++) {
switch (descrs[i].input_addr) {
case 0x0040: descrs[i].input_ptr = (const int16_t *)&rt->inputs.rpm; break;
case 0x0044: descrs[i].input_ptr = &rt->inputs.inj_qty_demand; break;
case 0x0046: descrs[i].input_ptr = &rt->active_request; break;
case 0x0142: descrs[i].input_ptr = (const int16_t *)&rt->inputs.supply_voltage; break;
default: descrs[i].input_ptr = NULL; break;
}
}
}
/* ══════════════════════════════════════════════════════════════════════
* PUBLIC API
* ══════════════════════════════════════════════════════════════════════ */
typedef struct pwm_flash { char _unused; } pwm_flash_t;
void pwm_init(pwm_runtime_t *rt,
const pwm_calibration_t *cal,
const pwm_flash_t *flash,
const pwm_input_getters_t *getters);
void pwm_service(pwm_runtime_t *rt);
static inline void pwm_ckp_isr(pwm_runtime_t *rt) { rt->reset_flag = 1; }
int16_t pwm_interp_lookup(const int16_t *x, const int16_t *y,
uint16_t n, int16_t in);
uint16_t pwm_lut_duty(const pwm_calibration_t *cal,
uint16_t rpm, int16_t fbkw);
extern const pwm_calibration_t pwm_cal_rom;
extern const pwm_flash_t pwm_flash_rom;
enum pwm_submap_id {
PWM_SUBMAP_SETPOINT_INTERP = 0,
PWM_SUBMAP_PWM_A = 1,
PWM_SUBMAP_PWM_B = 2,
PWM_SUBMAP_SHAPE_EVAL = 3,
PWM_SUBMAP_COUNT = 4
};
extern pwm_submap_descr_t pwm_submap_descrs[PWM_SUBMAP_COUNT];
extern const int16_t *pwm_submap_y_of(uint16_t idx);
#endif /* PWM_H */