307 lines
14 KiB
C
307 lines
14 KiB
C
/**
|
||
* @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 */
|